diff --git a/.gitignore b/.gitignore index e775376..4a4bf66 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ lib/ -modules/ fonts/ scripts/ include/ 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/app.log b/modules/app.log new file mode 100644 index 0000000..5164973 --- /dev/null +++ b/modules/app.log @@ -0,0 +1,3662 @@ +[2025-07-03 23:11:20,584] [MainThread] [DEBUG] [postImageManager.py:font_load:40] 폰트 로드 성공: D:\py\AutoPercenty3\src\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-07-03 23:11:20,585] [MainThread] [WARNING] [image_processor2.py:process_single_image:102] 이미지 1 처리 중단: 유효하지 않은 이미지 주소 - D:\py\AutoPercenty3\src\modules\img\1.jpg +[2025-07-03 23:11:20,597] [MainThread] [WARNING] [image_processor2.py:process_single_image:102] 이미지 2 처리 중단: 유효하지 않은 이미지 주소 - D:\py\AutoPercenty3\src\modules\img\2.jpg +[2025-07-03 23:11:20,612] [MainThread] [WARNING] [image_processor2.py:process_single_image:102] 이미지 3 처리 중단: 유효하지 않은 이미지 주소 - D:\py\AutoPercenty3\src\modules\img\3.jpg +[2025-07-03 23:11:20,625] [MainThread] [WARNING] [image_processor2.py:process_single_image:102] 이미지 4 처리 중단: 유효하지 않은 이미지 주소 - D:\py\AutoPercenty3\src\modules\img\4.jpg +[2025-07-03 23:11:20,634] [MainThread] [WARNING] [image_processor2.py:process_single_image:102] 이미지 5 처리 중단: 유효하지 않은 이미지 주소 - D:\py\AutoPercenty3\src\modules\img\5.jpg +[2025-07-03 23:11:20,644] [MainThread] [WARNING] [image_processor2.py:process_single_image:102] 이미지 6 처리 중단: 유효하지 않은 이미지 주소 - D:\py\AutoPercenty3\src\modules\img\6.jpg +[2025-07-03 23:11:20,656] [MainThread] [WARNING] [image_processor2.py:process_single_image:102] 이미지 7 처리 중단: 유효하지 않은 이미지 주소 - D:\py\AutoPercenty3\src\modules\img\7.jpg +[2025-07-03 23:11:20,667] [MainThread] [WARNING] [image_processor2.py:process_single_image:102] 이미지 1 처리 중단: 유효하지 않은 이미지 주소 - D:\py\AutoPercenty3\src\modules\img\1.jpg +[2025-07-03 23:11:20,667] [MainThread] [WARNING] [image_processor2.py:process_single_image:102] 이미지 2 처리 중단: 유효하지 않은 이미지 주소 - D:\py\AutoPercenty3\src\modules\img\2.jpg +[2025-07-03 23:11:20,668] [MainThread] [WARNING] [image_processor2.py:process_single_image:102] 이미지 3 처리 중단: 유효하지 않은 이미지 주소 - D:\py\AutoPercenty3\src\modules\img\3.jpg +[2025-07-03 23:11:20,668] [MainThread] [WARNING] [image_processor2.py:process_single_image:102] 이미지 4 처리 중단: 유효하지 않은 이미지 주소 - D:\py\AutoPercenty3\src\modules\img\4.jpg +[2025-07-03 23:11:20,668] [MainThread] [WARNING] [image_processor2.py:process_single_image:102] 이미지 5 처리 중단: 유효하지 않은 이미지 주소 - D:\py\AutoPercenty3\src\modules\img\5.jpg +[2025-07-03 23:11:20,669] [MainThread] [WARNING] [image_processor2.py:process_single_image:102] 이미지 6 처리 중단: 유효하지 않은 이미지 주소 - D:\py\AutoPercenty3\src\modules\img\6.jpg +[2025-07-03 23:11:20,669] [MainThread] [WARNING] [image_processor2.py:process_single_image:102] 이미지 7 처리 중단: 유효하지 않은 이미지 주소 - D:\py\AutoPercenty3\src\modules\img\7.jpg +[2025-07-03 23:15:35,812] [MainThread] [DEBUG] [postImageManager.py:font_load:40] 폰트 로드 성공: D:\py\AutoPercenty3\src\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-07-03 23:15:35,813] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 1 처리 시작: D:\py\AutoPercenty3\src\modules\img\1.jpg - OCR+인페인팅 모드 +[2025-07-03 23:15:35,814] [MainThread] [ERROR] [image_processor2.py:download_image:234] 이미지 다운로드 중 오류: 'NoneType' object has no attribute 'request' +[2025-07-03 23:15:35,815] [MainThread] [WARNING] [image_processor2.py:process_single_image:130] 이미지 1 다운로드 실패, 원본 URL 반환 +[2025-07-03 23:15:35,920] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 2 처리 시작: D:\py\AutoPercenty3\src\modules\img\2.jpg - OCR+인페인팅 모드 +[2025-07-03 23:15:35,923] [MainThread] [ERROR] [image_processor2.py:download_image:234] 이미지 다운로드 중 오류: 'NoneType' object has no attribute 'request' +[2025-07-03 23:15:35,924] [MainThread] [WARNING] [image_processor2.py:process_single_image:130] 이미지 2 다운로드 실패, 원본 URL 반환 +[2025-07-03 23:15:36,031] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 3 처리 시작: D:\py\AutoPercenty3\src\modules\img\3.jpg - OCR+인페인팅 모드 +[2025-07-03 23:15:36,032] [MainThread] [ERROR] [image_processor2.py:download_image:234] 이미지 다운로드 중 오류: 'NoneType' object has no attribute 'request' +[2025-07-03 23:15:36,033] [MainThread] [WARNING] [image_processor2.py:process_single_image:130] 이미지 3 다운로드 실패, 원본 URL 반환 +[2025-07-03 23:15:36,138] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 4 처리 시작: D:\py\AutoPercenty3\src\modules\img\4.jpg - OCR+인페인팅 모드 +[2025-07-03 23:15:36,139] [MainThread] [ERROR] [image_processor2.py:download_image:234] 이미지 다운로드 중 오류: 'NoneType' object has no attribute 'request' +[2025-07-03 23:15:36,141] [MainThread] [WARNING] [image_processor2.py:process_single_image:130] 이미지 4 다운로드 실패, 원본 URL 반환 +[2025-07-03 23:15:36,249] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 5 처리 시작: D:\py\AutoPercenty3\src\modules\img\5.jpg - OCR+인페인팅 모드 +[2025-07-03 23:15:36,250] [MainThread] [ERROR] [image_processor2.py:download_image:234] 이미지 다운로드 중 오류: 'NoneType' object has no attribute 'request' +[2025-07-03 23:15:36,250] [MainThread] [WARNING] [image_processor2.py:process_single_image:130] 이미지 5 다운로드 실패, 원본 URL 반환 +[2025-07-03 23:15:36,357] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 6 처리 시작: D:\py\AutoPercenty3\src\modules\img\6.jpg - OCR+인페인팅 모드 +[2025-07-03 23:15:36,358] [MainThread] [ERROR] [image_processor2.py:download_image:234] 이미지 다운로드 중 오류: 'NoneType' object has no attribute 'request' +[2025-07-03 23:15:36,359] [MainThread] [WARNING] [image_processor2.py:process_single_image:130] 이미지 6 다운로드 실패, 원본 URL 반환 +[2025-07-03 23:15:36,477] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 7 처리 시작: D:\py\AutoPercenty3\src\modules\img\7.jpg - OCR+인페인팅 모드 +[2025-07-03 23:15:36,481] [MainThread] [ERROR] [image_processor2.py:download_image:234] 이미지 다운로드 중 오류: 'NoneType' object has no attribute 'request' +[2025-07-03 23:15:36,482] [MainThread] [WARNING] [image_processor2.py:process_single_image:130] 이미지 7 다운로드 실패, 원본 URL 반환 +[2025-07-03 23:15:36,486] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 1 처리 시작: D:\py\AutoPercenty3\src\modules\img\1.jpg - OCR+인페인팅 모드 +[2025-07-03 23:15:36,486] [MainThread] [ERROR] [image_processor2.py:download_image:234] 이미지 다운로드 중 오류: 'NoneType' object has no attribute 'request' +[2025-07-03 23:15:36,488] [MainThread] [WARNING] [image_processor2.py:process_single_image:130] 이미지 1 다운로드 실패, 원본 URL 반환 +[2025-07-03 23:15:36,597] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 2 처리 시작: D:\py\AutoPercenty3\src\modules\img\2.jpg - OCR+인페인팅 모드 +[2025-07-03 23:15:36,629] [MainThread] [ERROR] [image_processor2.py:download_image:234] 이미지 다운로드 중 오류: 'NoneType' object has no attribute 'request' +[2025-07-03 23:15:36,630] [MainThread] [WARNING] [image_processor2.py:process_single_image:130] 이미지 2 다운로드 실패, 원본 URL 반환 +[2025-07-03 23:15:36,631] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 4 처리 시작: D:\py\AutoPercenty3\src\modules\img\4.jpg - OCR+인페인팅 모드 +[2025-07-03 23:15:36,633] [MainThread] [ERROR] [image_processor2.py:download_image:234] 이미지 다운로드 중 오류: 'NoneType' object has no attribute 'request' +[2025-07-03 23:15:36,633] [MainThread] [WARNING] [image_processor2.py:process_single_image:130] 이미지 4 다운로드 실패, 원본 URL 반환 +[2025-07-03 23:15:36,634] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 7 처리 시작: D:\py\AutoPercenty3\src\modules\img\7.jpg - OCR+인페인팅 모드 +[2025-07-03 23:15:36,634] [MainThread] [ERROR] [image_processor2.py:download_image:234] 이미지 다운로드 중 오류: 'NoneType' object has no attribute 'request' +[2025-07-03 23:15:36,635] [MainThread] [WARNING] [image_processor2.py:process_single_image:130] 이미지 7 다운로드 실패, 원본 URL 반환 +[2025-07-03 23:15:36,635] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 6 처리 시작: D:\py\AutoPercenty3\src\modules\img\6.jpg - OCR+인페인팅 모드 +[2025-07-03 23:15:36,635] [MainThread] [ERROR] [image_processor2.py:download_image:234] 이미지 다운로드 중 오류: 'NoneType' object has no attribute 'request' +[2025-07-03 23:15:36,636] [MainThread] [WARNING] [image_processor2.py:process_single_image:130] 이미지 6 다운로드 실패, 원본 URL 반환 +[2025-07-03 23:15:36,665] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 3 처리 시작: D:\py\AutoPercenty3\src\modules\img\3.jpg - OCR+인페인팅 모드 +[2025-07-03 23:15:36,667] [MainThread] [ERROR] [image_processor2.py:download_image:234] 이미지 다운로드 중 오류: 'NoneType' object has no attribute 'request' +[2025-07-03 23:15:36,668] [MainThread] [WARNING] [image_processor2.py:process_single_image:130] 이미지 3 다운로드 실패, 원본 URL 반환 +[2025-07-03 23:15:36,668] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 5 처리 시작: D:\py\AutoPercenty3\src\modules\img\5.jpg - OCR+인페인팅 모드 +[2025-07-03 23:15:36,668] [MainThread] [ERROR] [image_processor2.py:download_image:234] 이미지 다운로드 중 오류: 'NoneType' object has no attribute 'request' +[2025-07-03 23:15:36,670] [MainThread] [WARNING] [image_processor2.py:process_single_image:130] 이미지 5 다운로드 실패, 원본 URL 반환 +[2025-07-03 23:17:20,893] [MainThread] [DEBUG] [postImageManager.py:font_load:40] 폰트 로드 성공: D:\py\AutoPercenty3\src\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-07-03 23:17:20,894] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 1 처리 시작: D:\py\AutoPercenty3\src\modules\img\1.jpg - OCR+인페인팅 모드 +[2025-07-03 23:17:20,894] [MainThread] [INFO] [image_processor2.py:download_image:196] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\1.jpg +[2025-07-03 23:17:22,764] [MainThread] [ERROR] [image_processor2.py:process_single_image:170] 이미지 1 처리 중 오류: object list can't be used in 'await' expression +[2025-07-03 23:17:22,879] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 2 처리 시작: D:\py\AutoPercenty3\src\modules\img\2.jpg - OCR+인페인팅 모드 +[2025-07-03 23:17:22,880] [MainThread] [INFO] [image_processor2.py:download_image:196] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\2.jpg +[2025-07-03 23:17:23,859] [MainThread] [ERROR] [image_processor2.py:process_single_image:170] 이미지 2 처리 중 오류: object list can't be used in 'await' expression +[2025-07-03 23:17:23,968] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 3 처리 시작: D:\py\AutoPercenty3\src\modules\img\3.jpg - OCR+인페인팅 모드 +[2025-07-03 23:17:23,969] [MainThread] [INFO] [image_processor2.py:download_image:196] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\3.jpg +[2025-07-03 23:17:25,054] [MainThread] [ERROR] [image_processor2.py:process_single_image:170] 이미지 3 처리 중 오류: object list can't be used in 'await' expression +[2025-07-03 23:17:25,163] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 4 처리 시작: D:\py\AutoPercenty3\src\modules\img\4.jpg - OCR+인페인팅 모드 +[2025-07-03 23:17:25,163] [MainThread] [INFO] [image_processor2.py:download_image:196] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\4.jpg +[2025-07-03 23:17:26,497] [MainThread] [ERROR] [image_processor2.py:process_single_image:170] 이미지 4 처리 중 오류: object list can't be used in 'await' expression +[2025-07-03 23:17:26,605] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 5 처리 시작: D:\py\AutoPercenty3\src\modules\img\5.jpg - OCR+인페인팅 모드 +[2025-07-03 23:17:26,605] [MainThread] [INFO] [image_processor2.py:download_image:196] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\5.jpg +[2025-07-03 23:17:27,668] [MainThread] [ERROR] [image_processor2.py:process_single_image:170] 이미지 5 처리 중 오류: object list can't be used in 'await' expression +[2025-07-03 23:17:27,781] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 6 처리 시작: D:\py\AutoPercenty3\src\modules\img\6.jpg - OCR+인페인팅 모드 +[2025-07-03 23:17:27,781] [MainThread] [INFO] [image_processor2.py:download_image:196] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\6.jpg +[2025-07-03 23:17:28,605] [MainThread] [ERROR] [image_processor2.py:process_single_image:170] 이미지 6 처리 중 오류: object list can't be used in 'await' expression +[2025-07-03 23:17:28,724] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 7 처리 시작: D:\py\AutoPercenty3\src\modules\img\7.jpg - OCR+인페인팅 모드 +[2025-07-03 23:17:28,724] [MainThread] [INFO] [image_processor2.py:download_image:196] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\7.jpg +[2025-07-03 23:19:09,485] [MainThread] [DEBUG] [postImageManager.py:font_load:40] 폰트 로드 성공: D:\py\AutoPercenty3\src\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-07-03 23:19:09,486] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 1 처리 시작: D:\py\AutoPercenty3\src\modules\img\1.jpg - OCR+인페인팅 모드 +[2025-07-03 23:19:09,486] [MainThread] [INFO] [image_processor2.py:download_image:196] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\1.jpg +[2025-07-03 23:19:10,975] [MainThread] [INFO] [image_processor2.py:process_translated_texts:294] 최종 치환 결과: ['高质量水晶相纸_번역', '适合各种喷墨打印机_번역', '色彩艳丽还原度高_번역', 'Colors_번역', '高质量水晶相纸_번역', 'Beautiful彩丽_번역', '打印快干多种规格_번역', '12_번역', '4R_번역', '20_번역', '100_번역', 'SHEETS_번역', '230_번역', 'g/m"_번역'] +[2025-07-03 23:19:10,976] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 1 치환됨 +[2025-07-03 23:19:11,013] [MainThread] [ERROR] [image_processor2.py:process_single_image:170] 이미지 1 처리 중 오류: OpenCV(4.6.0) :-1: error: (-5:Bad argument) in function 'imencode' +> Overload resolution failed: +> - img is not a numpy array, neither a scalar +> - Expected Ptr for argument 'img' + +[2025-07-03 23:19:11,126] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 2 처리 시작: D:\py\AutoPercenty3\src\modules\img\2.jpg - OCR+인페인팅 모드 +[2025-07-03 23:19:11,128] [MainThread] [INFO] [image_processor2.py:download_image:196] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\2.jpg +[2025-07-03 23:19:11,846] [MainThread] [INFO] [image_processor2.py:process_translated_texts:294] 최종 치환 결과: ['WA汉世刘家_번역', '脱水比洗衣机更干_번역', '真正免手洗_번역'] +[2025-07-03 23:19:11,847] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 2 치환됨 +[2025-07-03 23:19:11,866] [MainThread] [ERROR] [image_processor2.py:process_single_image:170] 이미지 2 처리 중 오류: OpenCV(4.6.0) :-1: error: (-5:Bad argument) in function 'imencode' +> Overload resolution failed: +> - img is not a numpy array, neither a scalar +> - Expected Ptr for argument 'img' + +[2025-07-03 23:19:11,976] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 3 처리 시작: D:\py\AutoPercenty3\src\modules\img\3.jpg - OCR+인페인팅 모드 +[2025-07-03 23:19:11,976] [MainThread] [INFO] [image_processor2.py:download_image:196] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\3.jpg +[2025-07-03 23:19:12,891] [MainThread] [INFO] [image_processor2.py:process_translated_texts:294] 최종 치환 결과: ['MICROCULTIVATOR-_번역', '轻轻一拉_번역', '3秒即可快速启动_번역', '加快机器供油,燃烧,传动流程3秒快速启动_번역', '让您不用浪费时间在启动上_번역'] +[2025-07-03 23:19:12,891] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 3 치환됨 +[2025-07-03 23:19:12,907] [MainThread] [ERROR] [image_processor2.py:process_single_image:170] 이미지 3 처리 중 오류: OpenCV(4.6.0) :-1: error: (-5:Bad argument) in function 'imencode' +> Overload resolution failed: +> - img is not a numpy array, neither a scalar +> - Expected Ptr for argument 'img' + +[2025-07-03 23:19:13,013] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 4 처리 시작: D:\py\AutoPercenty3\src\modules\img\4.jpg - OCR+인페인팅 모드 +[2025-07-03 23:19:13,013] [MainThread] [INFO] [image_processor2.py:download_image:196] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\4.jpg +[2025-07-03 23:19:14,370] [MainThread] [INFO] [image_processor2.py:process_translated_texts:294] 최종 치환 결과: ['85°C_번역', '185°F_번역', '80℃_번역', '176F_번역', '70°℃_번역', '158F_번역', '60℃_번역', '140°F_번역', '50°C_번역', '122°F_번역', '40C_번역', '104°F_번역', '精确的温度控制_번역', '温度范围从30℃至85℃_번역', '30°C_번역', '86°F_번역', 'Temperature range from 86 F to 185°F_번역', 'PRECISE TEMPERATURECONTROL_번역'] +[2025-07-03 23:19:14,371] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 4 치환됨 +[2025-07-03 23:19:14,383] [MainThread] [ERROR] [image_processor2.py:process_single_image:170] 이미지 4 처리 중 오류: OpenCV(4.6.0) :-1: error: (-5:Bad argument) in function 'imencode' +> Overload resolution failed: +> - img is not a numpy array, neither a scalar +> - Expected Ptr for argument 'img' + +[2025-07-03 23:19:14,488] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 5 처리 시작: D:\py\AutoPercenty3\src\modules\img\5.jpg - OCR+인페인팅 모드 +[2025-07-03 23:19:14,488] [MainThread] [INFO] [image_processor2.py:download_image:196] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\5.jpg +[2025-07-03 23:19:15,496] [MainThread] [INFO] [image_processor2.py:process_translated_texts:294] 최종 치환 결과: ['现代极简风格_번역', '更易搭配各种使用场景_번역', '★WELCOME_번역', '欢迎光临_번역', '限时促销礼惠全城_번역', '满499减200/满999减500_번역', '动的间167_번역'] +[2025-07-03 23:19:15,497] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 5 치환됨 +[2025-07-03 23:19:15,532] [MainThread] [ERROR] [image_processor2.py:process_single_image:170] 이미지 5 처리 중 오류: OpenCV(4.6.0) :-1: error: (-5:Bad argument) in function 'imencode' +> Overload resolution failed: +> - img is not a numpy array, neither a scalar +> - Expected Ptr for argument 'img' + +[2025-07-03 23:19:15,649] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 6 처리 시작: D:\py\AutoPercenty3\src\modules\img\6.jpg - OCR+인페인팅 모드 +[2025-07-03 23:19:15,649] [MainThread] [INFO] [image_processor2.py:download_image:196] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\6.jpg +[2025-07-03 23:19:16,476] [MainThread] [INFO] [image_processor2.py:process_translated_texts:294] 최종 치환 결과: ['现代极简风格_번역', '更易搭配各种使用场景_번역', '半圆两端设计_번역', '承载各种欢乐_번역'] +[2025-07-03 23:19:16,476] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 6 치환됨 +[2025-07-03 23:19:16,516] [MainThread] [ERROR] [image_processor2.py:process_single_image:170] 이미지 6 처리 중 오류: OpenCV(4.6.0) :-1: error: (-5:Bad argument) in function 'imencode' +> Overload resolution failed: +> - img is not a numpy array, neither a scalar +> - Expected Ptr for argument 'img' + +[2025-07-03 23:32:57,907] [MainThread] [INFO] [iop_Manager.py:_start_instances:72] IOPaint 인스턴스 1 개 시작 +[2025-07-03 23:32:57,908] [MainThread] [INFO] [iop_Manager.py:_start_instances:76] [8129] 인스턴스 실행 명령: D:\py\AutoPercenty3\src\modules\iop\iop.exe start --model=lama --device=cpu --port 8129 --model-dir D:\py\AutoPercenty3\src\modules\iop\models +[2025-07-03 23:32:57,917] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:51] [8129] 서버 준비 체크 시작 (최대 30초 대기) +[2025-07-03 23:33:01,966] [MainThread] [ERROR] [iop_Manager.py:wait_for_server_ready:66] [8129] 준비 체크 실패 (시도 1회): HTTPConnectionPool(host='localhost', port=8129): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 198, in _new_conn + sock = connection.create_connection( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\connection.py", line 85, in create_connection + raise err + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\connection.py", line 73, in create_connection + sock.connect(sa) +TimeoutError: timed out + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 787, in urlopen + response = self._make_request( + ^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 493, in _make_request + conn.request( + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 494, in request + self.endheaders() + File "D:\Python311\Lib\http\client.py", line 1298, in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + File "D:\Python311\Lib\http\client.py", line 1058, in _send_output + self.send(msg) + File "D:\Python311\Lib\http\client.py", line 996, in send + self.connect() + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 325, in connect + self.sock = self._new_conn() + ^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 207, in _new_conn + raise ConnectTimeoutError( +urllib3.exceptions.ConnectTimeoutError: (, 'Connection to localhost timed out. (connect timeout=2)') + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\requests\adapters.py", line 667, in send + resp = conn.urlopen( + ^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 841, in urlopen + retries = retries.increment( + ^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\retry.py", line 519, in increment + raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8129): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\iop_Manager.py", line 56, in wait_for_server_ready + r = requests.get(url, timeout=2) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\api.py", line 73, in get + return request("get", url, params=params, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\api.py", line 59, in request + return session.request(method=method, url=url, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\sessions.py", line 589, in request + resp = self.send(prep, **send_kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\sessions.py", line 703, in send + r = adapter.send(request, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\adapters.py", line 688, in send + raise ConnectTimeout(e, request=request) +requests.exceptions.ConnectTimeout: HTTPConnectionPool(host='localhost', port=8129): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) + +[2025-07-03 23:33:06,491] [MainThread] [ERROR] [iop_Manager.py:wait_for_server_ready:66] [8129] 준비 체크 실패 (시도 2회): HTTPConnectionPool(host='localhost', port=8129): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 198, in _new_conn + sock = connection.create_connection( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\connection.py", line 85, in create_connection + raise err + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\connection.py", line 73, in create_connection + sock.connect(sa) +TimeoutError: timed out + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 787, in urlopen + response = self._make_request( + ^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 493, in _make_request + conn.request( + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 494, in request + self.endheaders() + File "D:\Python311\Lib\http\client.py", line 1298, in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + File "D:\Python311\Lib\http\client.py", line 1058, in _send_output + self.send(msg) + File "D:\Python311\Lib\http\client.py", line 996, in send + self.connect() + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 325, in connect + self.sock = self._new_conn() + ^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 207, in _new_conn + raise ConnectTimeoutError( +urllib3.exceptions.ConnectTimeoutError: (, 'Connection to localhost timed out. (connect timeout=2)') + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\requests\adapters.py", line 667, in send + resp = conn.urlopen( + ^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 841, in urlopen + retries = retries.increment( + ^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\retry.py", line 519, in increment + raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8129): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\iop_Manager.py", line 56, in wait_for_server_ready + r = requests.get(url, timeout=2) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\api.py", line 73, in get + return request("get", url, params=params, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\api.py", line 59, in request + return session.request(method=method, url=url, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\sessions.py", line 589, in request + resp = self.send(prep, **send_kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\sessions.py", line 703, in send + r = adapter.send(request, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\adapters.py", line 688, in send + raise ConnectTimeout(e, request=request) +requests.exceptions.ConnectTimeout: HTTPConnectionPool(host='localhost', port=8129): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) + +[2025-07-03 23:33:09,024] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:57] 응답 : +[2025-07-03 23:33:09,025] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:60] [8129] 서버 준비 완료! (시도 3회, 11.1초 소요) +[2025-07-03 23:33:09,026] [MainThread] [INFO] [iop_Manager.py:_start_instances:81] IOPaint 인스턴스 8129 준비됨 +[2025-07-03 23:33:17,727] [MainThread] [DEBUG] [postImageManager.py:font_load:40] 폰트 로드 성공: D:\py\AutoPercenty3\src\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-07-03 23:33:17,728] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 1 처리 시작: D:\py\AutoPercenty3\src\modules\img\1.jpg - OCR+인페인팅 모드 +[2025-07-03 23:33:17,729] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\1.jpg +[2025-07-03 23:33:19,210] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['高质量水晶相纸_번역', '适合各种喷墨打印机_번역', '色彩艳丽还原度高_번역', 'Colors_번역', '高质量水晶相纸_번역', 'Beautiful彩丽_번역', '打印快干多种规格_번역', '12_번역', '4R_번역', '20_번역', '100_번역', 'SHEETS_번역', '230_번역', 'g/m"_번역'] +[2025-07-03 23:33:19,210] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 1 치환됨 +[2025-07-03 23:33:19,234] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:33:19,244] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:110] IOPaint 인스턴스 8129 사용 중 +[2025-07-03 23:33:19,245] [MainThread] [INFO] [iop_Manager.py:inpaint:158] IOPaint 인스턴스 8129 사용 +[2025-07-03 23:33:27,773] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:119] IOPaint 인스턴스 8129 유휴 +[2025-07-03 23:33:27,775] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + ... + + [[156 192 240] + [159 195 243] + [164 197 246] + ... + [162 202 237] + [161 201 236] + [160 200 235]] + + [[154 191 241] + [156 191 241] + [159 192 242] + ... + [167 207 242] + [163 203 238] + [158 198 233]] + + [[154 191 241] + [152 189 239] + [153 186 236] + ... + [175 215 250] + [168 208 243] + [159 199 234]]] +[2025-07-03 23:33:27,900] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + ... + + [[156 192 240] + [159 195 243] + [164 197 246] + ... + [162 202 237] + [161 201 236] + [160 200 235]] + + [[154 191 241] + [156 191 241] + [159 192 242] + ... + [167 207 242] + [163 203 238] + [158 198 233]] + + [[154 191 241] + [152 189 239] + [153 186 236] + ... + [175 215 250] + [168 208 243] + [159 199 234]]] +[2025-07-03 23:33:27,901] [MainThread] [ERROR] [image_processor2.py:postProcess_and_save_image:192] 이미지 1 번역 처리 중 오류: PostImageManager.add_watermark() got an unexpected keyword argument 'image' +[2025-07-03 23:33:27,902] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 1 번역 완료: D:\py\AutoPercenty3\src\modules\img\1.jpg +[2025-07-03 23:33:28,008] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 2 처리 시작: D:\py\AutoPercenty3\src\modules\img\2.jpg - OCR+인페인팅 모드 +[2025-07-03 23:33:28,009] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\2.jpg +[2025-07-03 23:33:28,730] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['WA汉世刘家_번역', '脱水比洗衣机更干_번역', '真正免手洗_번역'] +[2025-07-03 23:33:28,731] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 2 치환됨 +[2025-07-03 23:33:28,750] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:33:28,768] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:110] IOPaint 인스턴스 8129 사용 중 +[2025-07-03 23:33:28,768] [MainThread] [INFO] [iop_Manager.py:inpaint:158] IOPaint 인스턴스 8129 사용 +[2025-07-03 23:33:35,961] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:119] IOPaint 인스턴스 8129 유휴 +[2025-07-03 23:33:35,963] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]]] +[2025-07-03 23:33:36,021] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]]] +[2025-07-03 23:33:36,022] [MainThread] [ERROR] [image_processor2.py:postProcess_and_save_image:192] 이미지 2 번역 처리 중 오류: PostImageManager.add_watermark() got an unexpected keyword argument 'image' +[2025-07-03 23:33:36,022] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 2 번역 완료: D:\py\AutoPercenty3\src\modules\img\2.jpg +[2025-07-03 23:33:36,139] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 3 처리 시작: D:\py\AutoPercenty3\src\modules\img\3.jpg - OCR+인페인팅 모드 +[2025-07-03 23:33:36,140] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\3.jpg +[2025-07-03 23:33:37,101] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['MICROCULTIVATOR-_번역', '轻轻一拉_번역', '3秒即可快速启动_번역', '加快机器供油,燃烧,传动流程3秒快速启动_번역', '让您不用浪费时间在启动上_번역'] +[2025-07-03 23:33:37,101] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 3 치환됨 +[2025-07-03 23:33:37,117] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:33:37,129] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:110] IOPaint 인스턴스 8129 사용 중 +[2025-07-03 23:33:37,130] [MainThread] [INFO] [iop_Manager.py:inpaint:158] IOPaint 인스턴스 8129 사용 +[2025-07-03 23:36:16,503] [MainThread] [INFO] [iop_Manager.py:_start_instances:72] IOPaint 인스턴스 1 개 시작 +[2025-07-03 23:36:16,503] [MainThread] [INFO] [iop_Manager.py:_start_instances:76] [8166] 인스턴스 실행 명령: D:\py\AutoPercenty3\src\modules\iop\iop.exe start --model=lama --device=cpu --port 8166 --model-dir D:\py\AutoPercenty3\src\modules\iop\models +[2025-07-03 23:36:16,513] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:51] [8166] 서버 준비 체크 시작 (최대 30초 대기) +[2025-07-03 23:36:20,554] [MainThread] [ERROR] [iop_Manager.py:wait_for_server_ready:66] [8166] 준비 체크 실패 (시도 1회): HTTPConnectionPool(host='localhost', port=8166): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 198, in _new_conn + sock = connection.create_connection( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\connection.py", line 85, in create_connection + raise err + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\connection.py", line 73, in create_connection + sock.connect(sa) +TimeoutError: timed out + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 787, in urlopen + response = self._make_request( + ^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 493, in _make_request + conn.request( + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 494, in request + self.endheaders() + File "D:\Python311\Lib\http\client.py", line 1298, in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + File "D:\Python311\Lib\http\client.py", line 1058, in _send_output + self.send(msg) + File "D:\Python311\Lib\http\client.py", line 996, in send + self.connect() + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 325, in connect + self.sock = self._new_conn() + ^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 207, in _new_conn + raise ConnectTimeoutError( +urllib3.exceptions.ConnectTimeoutError: (, 'Connection to localhost timed out. (connect timeout=2)') + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\requests\adapters.py", line 667, in send + resp = conn.urlopen( + ^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 841, in urlopen + retries = retries.increment( + ^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\retry.py", line 519, in increment + raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8166): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\iop_Manager.py", line 56, in wait_for_server_ready + r = requests.get(url, timeout=2) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\api.py", line 73, in get + return request("get", url, params=params, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\api.py", line 59, in request + return session.request(method=method, url=url, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\sessions.py", line 589, in request + resp = self.send(prep, **send_kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\sessions.py", line 703, in send + r = adapter.send(request, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\adapters.py", line 688, in send + raise ConnectTimeout(e, request=request) +requests.exceptions.ConnectTimeout: HTTPConnectionPool(host='localhost', port=8166): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) + +[2025-07-03 23:36:25,075] [MainThread] [ERROR] [iop_Manager.py:wait_for_server_ready:66] [8166] 준비 체크 실패 (시도 2회): HTTPConnectionPool(host='localhost', port=8166): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 198, in _new_conn + sock = connection.create_connection( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\connection.py", line 85, in create_connection + raise err + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\connection.py", line 73, in create_connection + sock.connect(sa) +TimeoutError: timed out + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 787, in urlopen + response = self._make_request( + ^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 493, in _make_request + conn.request( + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 494, in request + self.endheaders() + File "D:\Python311\Lib\http\client.py", line 1298, in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + File "D:\Python311\Lib\http\client.py", line 1058, in _send_output + self.send(msg) + File "D:\Python311\Lib\http\client.py", line 996, in send + self.connect() + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 325, in connect + self.sock = self._new_conn() + ^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 207, in _new_conn + raise ConnectTimeoutError( +urllib3.exceptions.ConnectTimeoutError: (, 'Connection to localhost timed out. (connect timeout=2)') + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\requests\adapters.py", line 667, in send + resp = conn.urlopen( + ^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 841, in urlopen + retries = retries.increment( + ^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\retry.py", line 519, in increment + raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8166): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\iop_Manager.py", line 56, in wait_for_server_ready + r = requests.get(url, timeout=2) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\api.py", line 73, in get + return request("get", url, params=params, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\api.py", line 59, in request + return session.request(method=method, url=url, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\sessions.py", line 589, in request + resp = self.send(prep, **send_kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\sessions.py", line 703, in send + r = adapter.send(request, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\adapters.py", line 688, in send + raise ConnectTimeout(e, request=request) +requests.exceptions.ConnectTimeout: HTTPConnectionPool(host='localhost', port=8166): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) + +[2025-07-03 23:36:27,615] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:57] 응답 : +[2025-07-03 23:36:27,615] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:60] [8166] 서버 준비 완료! (시도 3회, 11.1초 소요) +[2025-07-03 23:36:27,615] [MainThread] [INFO] [iop_Manager.py:_start_instances:81] IOPaint 인스턴스 8166 준비됨 +[2025-07-03 23:36:35,991] [MainThread] [DEBUG] [postImageManager.py:font_load:40] 폰트 로드 성공: D:\py\AutoPercenty3\src\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-07-03 23:36:35,992] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 1 처리 시작: D:\py\AutoPercenty3\src\modules\img\1.jpg - OCR+인페인팅 모드 +[2025-07-03 23:36:35,993] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\1.jpg +[2025-07-03 23:36:37,700] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['高质量水晶相纸_번역', '适合各种喷墨打印机_번역', '色彩艳丽还原度高_번역', 'Colors_번역', '高质量水晶相纸_번역', 'Beautiful彩丽_번역', '打印快干多种规格_번역', '12_번역', '4R_번역', '20_번역', '100_번역', 'SHEETS_번역', '230_번역', 'g/m"_번역'] +[2025-07-03 23:36:37,701] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 1 치환됨 +[2025-07-03 23:36:37,716] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:36:37,726] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:110] IOPaint 인스턴스 8166 사용 중 +[2025-07-03 23:36:37,726] [MainThread] [INFO] [iop_Manager.py:inpaint:158] IOPaint 인스턴스 8166 사용 +[2025-07-03 23:36:47,532] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:119] IOPaint 인스턴스 8166 유휴 +[2025-07-03 23:36:47,533] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + ... + + [[156 192 240] + [159 195 243] + [164 197 246] + ... + [162 202 237] + [161 201 236] + [160 200 235]] + + [[154 191 241] + [156 191 241] + [159 192 242] + ... + [167 207 242] + [163 203 238] + [158 198 233]] + + [[154 191 241] + [152 189 239] + [153 186 236] + ... + [175 215 250] + [168 208 243] + [159 199 234]]] +[2025-07-03 23:36:47,638] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + ... + + [[156 192 240] + [159 195 243] + [164 197 246] + ... + [162 202 237] + [161 201 236] + [160 200 235]] + + [[154 191 241] + [156 191 241] + [159 192 242] + ... + [167 207 242] + [163 203 238] + [158 198 233]] + + [[154 191 241] + [152 189 239] + [153 186 236] + ... + [175 215 250] + [168 208 243] + [159 199 234]]] +[2025-07-03 23:36:47,640] [MainThread] [ERROR] [image_processor2.py:postProcess_and_save_image:192] 이미지 1 번역 처리 중 오류: 'numpy.ndarray' object has no attribute 'load' +[2025-07-03 23:36:47,641] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 1 번역 완료: D:\py\AutoPercenty3\src\modules\img\1.jpg +[2025-07-03 23:36:47,748] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 2 처리 시작: D:\py\AutoPercenty3\src\modules\img\2.jpg - OCR+인페인팅 모드 +[2025-07-03 23:36:47,750] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\2.jpg +[2025-07-03 23:36:48,477] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['WA汉世刘家_번역', '脱水比洗衣机更干_번역', '真正免手洗_번역'] +[2025-07-03 23:36:48,477] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 2 치환됨 +[2025-07-03 23:36:48,496] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:36:48,513] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:110] IOPaint 인스턴스 8166 사용 중 +[2025-07-03 23:36:48,514] [MainThread] [INFO] [iop_Manager.py:inpaint:158] IOPaint 인스턴스 8166 사용 +[2025-07-03 23:36:55,161] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:119] IOPaint 인스턴스 8166 유휴 +[2025-07-03 23:36:55,162] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]]] +[2025-07-03 23:36:55,189] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]]] +[2025-07-03 23:36:55,192] [MainThread] [ERROR] [image_processor2.py:postProcess_and_save_image:192] 이미지 2 번역 처리 중 오류: 'numpy.ndarray' object has no attribute 'load' +[2025-07-03 23:36:55,192] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 2 번역 완료: D:\py\AutoPercenty3\src\modules\img\2.jpg +[2025-07-03 23:36:55,309] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 3 처리 시작: D:\py\AutoPercenty3\src\modules\img\3.jpg - OCR+인페인팅 모드 +[2025-07-03 23:36:55,310] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\3.jpg +[2025-07-03 23:36:56,484] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['MICROCULTIVATOR-_번역', '轻轻一拉_번역', '3秒即可快速启动_번역', '加快机器供油,燃烧,传动流程3秒快速启动_번역', '让您不用浪费时间在启动上_번역'] +[2025-07-03 23:36:56,485] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 3 치환됨 +[2025-07-03 23:36:56,502] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:36:56,515] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:110] IOPaint 인스턴스 8166 사용 중 +[2025-07-03 23:36:56,515] [MainThread] [INFO] [iop_Manager.py:inpaint:158] IOPaint 인스턴스 8166 사용 +[2025-07-03 23:37:04,842] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:119] IOPaint 인스턴스 8166 유휴 +[2025-07-03 23:37:04,845] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[ 47 60 58] + [ 50 63 61] + [ 53 66 64] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + [[ 47 60 58] + [ 50 63 61] + [ 53 66 64] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + [[ 47 60 58] + [ 50 63 61] + [ 52 65 63] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + ... + + [[ 41 51 69] + [ 92 102 120] + [103 113 131] + ... + [ 76 85 105] + [ 99 108 128] + [116 125 145]] + + [[ 51 61 79] + [ 82 92 110] + [109 119 137] + ... + [ 79 88 108] + [128 137 157] + [135 144 164]] + + [[ 15 25 43] + [ 9 19 37] + [ 21 31 49] + ... + [ 10 19 39] + [ 29 38 58] + [ 23 32 52]]] +[2025-07-03 23:37:04,895] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[ 47 60 58] + [ 50 63 61] + [ 53 66 64] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + [[ 47 60 58] + [ 50 63 61] + [ 53 66 64] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + [[ 47 60 58] + [ 50 63 61] + [ 52 65 63] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + ... + + [[ 41 51 69] + [ 92 102 120] + [103 113 131] + ... + [ 76 85 105] + [ 99 108 128] + [116 125 145]] + + [[ 51 61 79] + [ 82 92 110] + [109 119 137] + ... + [ 79 88 108] + [128 137 157] + [135 144 164]] + + [[ 15 25 43] + [ 9 19 37] + [ 21 31 49] + ... + [ 10 19 39] + [ 29 38 58] + [ 23 32 52]]] +[2025-07-03 23:37:04,897] [MainThread] [ERROR] [image_processor2.py:postProcess_and_save_image:192] 이미지 3 번역 처리 중 오류: 'numpy.ndarray' object has no attribute 'load' +[2025-07-03 23:37:04,897] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 3 번역 완료: D:\py\AutoPercenty3\src\modules\img\3.jpg +[2025-07-03 23:37:05,005] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 4 처리 시작: D:\py\AutoPercenty3\src\modules\img\4.jpg - OCR+인페인팅 모드 +[2025-07-03 23:37:05,006] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\4.jpg +[2025-07-03 23:37:08,450] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['85°C_번역', '185°F_번역', '80℃_번역', '176F_번역', '70°℃_번역', '158F_번역', '60℃_번역', '140°F_번역', '50°C_번역', '122°F_번역', '40C_번역', '104°F_번역', '精确的温度控制_번역', '温度范围从30℃至85℃_번역', '30°C_번역', '86°F_번역', 'Temperature range from 86 F to 185°F_번역', 'PRECISE TEMPERATURECONTROL_번역'] +[2025-07-03 23:37:08,451] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 4 치환됨 +[2025-07-03 23:37:08,475] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:37:08,488] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:110] IOPaint 인스턴스 8166 사용 중 +[2025-07-03 23:37:08,489] [MainThread] [INFO] [iop_Manager.py:inpaint:158] IOPaint 인스턴스 8166 사용 +[2025-07-03 23:37:15,464] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:119] IOPaint 인스턴스 8166 유휴 +[2025-07-03 23:37:15,466] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [190 185 186]] + + [[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [190 185 186]] + + [[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [189 184 185]] + + ... + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 1 9] + [ 0 1 9]] + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 2 7] + [ 0 2 7]] + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 2 7] + [ 0 2 7]]] +[2025-07-03 23:37:15,597] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [190 185 186]] + + [[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [190 185 186]] + + [[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [189 184 185]] + + ... + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 1 9] + [ 0 1 9]] + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 2 7] + [ 0 2 7]] + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 2 7] + [ 0 2 7]]] +[2025-07-03 23:37:15,600] [MainThread] [ERROR] [image_processor2.py:postProcess_and_save_image:192] 이미지 4 번역 처리 중 오류: 'numpy.ndarray' object has no attribute 'load' +[2025-07-03 23:37:15,602] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 4 번역 완료: D:\py\AutoPercenty3\src\modules\img\4.jpg +[2025-07-03 23:37:15,716] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 5 처리 시작: D:\py\AutoPercenty3\src\modules\img\5.jpg - OCR+인페인팅 모드 +[2025-07-03 23:37:15,717] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\5.jpg +[2025-07-03 23:37:17,631] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['现代极简风格_번역', '更易搭配各种使用场景_번역', '★WELCOME_번역', '欢迎光临_번역', '限时促销礼惠全城_번역', '满499减200/满999减500_번역', '动的间167_번역'] +[2025-07-03 23:37:17,632] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 5 치환됨 +[2025-07-03 23:37:17,693] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:37:17,740] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:110] IOPaint 인스턴스 8166 사용 중 +[2025-07-03 23:37:17,741] [MainThread] [INFO] [iop_Manager.py:inpaint:158] IOPaint 인스턴스 8166 사용 +[2025-07-03 23:37:21,944] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:119] IOPaint 인스턴스 8166 유휴 +[2025-07-03 23:37:21,945] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 5 처리 중 오류: HTTPConnectionPool(host='localhost', port=8166): Max retries exceeded with url: /api/v1/inpaint (Caused by NewConnectionError(': Failed to establish a new connection: [WinError 10061] 대상 컴퓨터에서 연결을 거부했으므로 연결하지 못했습니다')) +[2025-07-03 23:48:37,010] [MainThread] [INFO] [iop_Manager.py:_start_instances:72] IOPaint 인스턴스 1 개 시작 +[2025-07-03 23:48:37,012] [MainThread] [INFO] [iop_Manager.py:_start_instances:76] [8132] 인스턴스 실행 명령: D:\py\AutoPercenty3\src\modules\iop\iop.exe start --model=lama --device=cpu --port 8132 --model-dir D:\py\AutoPercenty3\src\modules\iop\models +[2025-07-03 23:48:45,026] [MainThread] [INFO] [iop_Manager.py:_start_instances:82] [8132] 인스턴스 실행 명시대기: 8초 +[2025-07-03 23:48:45,026] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:51] [8132] 서버 준비 체크 시작 (최대 30초 대기) +[2025-07-03 23:48:49,083] [MainThread] [ERROR] [iop_Manager.py:wait_for_server_ready:66] [8132] 준비 체크 실패 (시도 1회): HTTPConnectionPool(host='localhost', port=8132): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 198, in _new_conn + sock = connection.create_connection( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\connection.py", line 85, in create_connection + raise err + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\connection.py", line 73, in create_connection + sock.connect(sa) +TimeoutError: timed out + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 787, in urlopen + response = self._make_request( + ^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 493, in _make_request + conn.request( + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 494, in request + self.endheaders() + File "D:\Python311\Lib\http\client.py", line 1298, in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + File "D:\Python311\Lib\http\client.py", line 1058, in _send_output + self.send(msg) + File "D:\Python311\Lib\http\client.py", line 996, in send + self.connect() + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 325, in connect + self.sock = self._new_conn() + ^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 207, in _new_conn + raise ConnectTimeoutError( +urllib3.exceptions.ConnectTimeoutError: (, 'Connection to localhost timed out. (connect timeout=2)') + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\requests\adapters.py", line 667, in send + resp = conn.urlopen( + ^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 841, in urlopen + retries = retries.increment( + ^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\retry.py", line 519, in increment + raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8132): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\iop_Manager.py", line 56, in wait_for_server_ready + r = requests.get(url, timeout=2) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\api.py", line 73, in get + return request("get", url, params=params, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\api.py", line 59, in request + return session.request(method=method, url=url, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\sessions.py", line 589, in request + resp = self.send(prep, **send_kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\sessions.py", line 703, in send + r = adapter.send(request, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\adapters.py", line 688, in send + raise ConnectTimeout(e, request=request) +requests.exceptions.ConnectTimeout: HTTPConnectionPool(host='localhost', port=8132): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) + +[2025-07-03 23:48:51,616] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:57] 응답 : +[2025-07-03 23:48:51,617] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:60] [8132] 서버 준비 완료! (시도 2회, 6.6초 소요) +[2025-07-03 23:48:51,617] [MainThread] [INFO] [iop_Manager.py:_start_instances:84] IOPaint 인스턴스 8132 준비됨 +[2025-07-03 23:49:04,431] [MainThread] [DEBUG] [postImageManager.py:font_load:40] 폰트 로드 성공: D:\py\AutoPercenty3\src\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-07-03 23:49:04,434] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 1 처리 시작: D:\py\AutoPercenty3\src\modules\img\1.jpg - OCR+인페인팅 모드 +[2025-07-03 23:49:04,435] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\1.jpg +[2025-07-03 23:49:07,045] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['高质量水晶相纸_번역', '适合各种喷墨打印机_번역', '色彩艳丽还原度高_번역', 'Colors_번역', '高质量水晶相纸_번역', 'Beautiful彩丽_번역', '打印快干多种规格_번역', '12_번역', '4R_번역', '20_번역', '100_번역', 'SHEETS_번역', '230_번역', 'g/m"_번역'] +[2025-07-03 23:49:07,045] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 1 치환됨 +[2025-07-03 23:49:07,073] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:49:07,092] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8132 사용 중 +[2025-07-03 23:49:07,093] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8132 사용 +[2025-07-03 23:49:18,649] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8132 유휴 +[2025-07-03 23:49:18,651] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + ... + + [[156 192 240] + [159 195 243] + [164 197 246] + ... + [162 202 237] + [161 201 236] + [160 200 235]] + + [[154 191 241] + [156 191 241] + [159 192 242] + ... + [167 207 242] + [163 203 238] + [158 198 233]] + + [[154 191 241] + [152 189 239] + [153 186 236] + ... + [175 215 250] + [168 208 243] + [159 199 234]]] +[2025-07-03 23:49:18,834] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + ... + + [[156 192 240] + [159 195 243] + [164 197 246] + ... + [162 202 237] + [161 201 236] + [160 200 235]] + + [[154 191 241] + [156 191 241] + [159 192 242] + ... + [167 207 242] + [163 203 238] + [158 198 233]] + + [[154 191 241] + [152 189 239] + [153 186 236] + ... + [175 215 250] + [168 208 243] + [159 199 234]]] +[2025-07-03 23:49:18,893] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_seq_img_1.png +[2025-07-03 23:49:19,084] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 1 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_seq_img_1.png +[2025-07-03 23:49:19,216] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 2 처리 시작: D:\py\AutoPercenty3\src\modules\img\2.jpg - OCR+인페인팅 모드 +[2025-07-03 23:49:19,217] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\2.jpg +[2025-07-03 23:49:20,683] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['WA汉世刘家_번역', '脱水比洗衣机更干_번역', '真正免手洗_번역'] +[2025-07-03 23:49:20,684] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 2 치환됨 +[2025-07-03 23:49:20,723] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:49:20,756] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8132 사용 중 +[2025-07-03 23:49:20,757] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8132 사용 +[2025-07-03 23:49:29,971] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8132 유휴 +[2025-07-03 23:49:29,974] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]]] +[2025-07-03 23:49:30,033] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]]] +[2025-07-03 23:49:30,088] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_seq_img_2.png +[2025-07-03 23:49:30,249] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 2 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_seq_img_2.png +[2025-07-03 23:49:30,375] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 3 처리 시작: D:\py\AutoPercenty3\src\modules\img\3.jpg - OCR+인페인팅 모드 +[2025-07-03 23:49:30,376] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\3.jpg +[2025-07-03 23:49:32,164] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['MICROCULTIVATOR-_번역', '轻轻一拉_번역', '3秒即可快速启动_번역', '加快机器供油,燃烧,传动流程3秒快速启动_번역', '让您不用浪费时间在启动上_번역'] +[2025-07-03 23:49:32,165] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 3 치환됨 +[2025-07-03 23:49:32,196] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:49:32,222] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8132 사용 중 +[2025-07-03 23:49:32,223] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8132 사용 +[2025-07-03 23:56:30,356] [MainThread] [INFO] [iop_Manager.py:_start_instances:72] IOPaint 인스턴스 1 개 시작 +[2025-07-03 23:56:30,357] [MainThread] [INFO] [iop_Manager.py:_start_instances:76] [8189] 인스턴스 실행 명령: D:\py\AutoPercenty3\src\modules\iop\iop.exe start --model=lama --device=cpu --port 8189 --model-dir D:\py\AutoPercenty3\src\modules\iop\models +[2025-07-03 23:56:38,371] [MainThread] [INFO] [iop_Manager.py:_start_instances:82] [8189] 인스턴스 실행 명시대기: 8초 +[2025-07-03 23:56:38,371] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:51] [8189] 서버 준비 체크 시작 (최대 30초 대기) +[2025-07-03 23:56:42,417] [MainThread] [ERROR] [iop_Manager.py:wait_for_server_ready:66] [8189] 준비 체크 실패 (시도 1회): HTTPConnectionPool(host='localhost', port=8189): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 198, in _new_conn + sock = connection.create_connection( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\connection.py", line 85, in create_connection + raise err + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\connection.py", line 73, in create_connection + sock.connect(sa) +TimeoutError: timed out + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 787, in urlopen + response = self._make_request( + ^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 493, in _make_request + conn.request( + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 494, in request + self.endheaders() + File "D:\Python311\Lib\http\client.py", line 1298, in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + File "D:\Python311\Lib\http\client.py", line 1058, in _send_output + self.send(msg) + File "D:\Python311\Lib\http\client.py", line 996, in send + self.connect() + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 325, in connect + self.sock = self._new_conn() + ^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 207, in _new_conn + raise ConnectTimeoutError( +urllib3.exceptions.ConnectTimeoutError: (, 'Connection to localhost timed out. (connect timeout=2)') + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\requests\adapters.py", line 667, in send + resp = conn.urlopen( + ^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 841, in urlopen + retries = retries.increment( + ^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\retry.py", line 519, in increment + raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8189): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\iop_Manager.py", line 56, in wait_for_server_ready + r = requests.get(url, timeout=2) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\api.py", line 73, in get + return request("get", url, params=params, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\api.py", line 59, in request + return session.request(method=method, url=url, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\sessions.py", line 589, in request + resp = self.send(prep, **send_kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\sessions.py", line 703, in send + r = adapter.send(request, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\adapters.py", line 688, in send + raise ConnectTimeout(e, request=request) +requests.exceptions.ConnectTimeout: HTTPConnectionPool(host='localhost', port=8189): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) + +[2025-07-03 23:56:44,952] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:57] 응답 : +[2025-07-03 23:56:44,956] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:60] [8189] 서버 준비 완료! (시도 2회, 6.6초 소요) +[2025-07-03 23:56:44,957] [MainThread] [INFO] [iop_Manager.py:_start_instances:84] IOPaint 인스턴스 8189 준비됨 +[2025-07-03 23:56:55,957] [MainThread] [DEBUG] [postImageManager.py:font_load:40] 폰트 로드 성공: D:\py\AutoPercenty3\src\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-07-03 23:56:55,960] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 1 처리 시작: D:\py\AutoPercenty3\src\modules\img\1.jpg - OCR+인페인팅 모드 +[2025-07-03 23:56:55,960] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\1.jpg +[2025-07-03 23:57:02,178] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['고품질 크리스탈 사진지', '모든 잉크젯 프린터에 적합', '색상이 선명하고 재현도가 높음', '색상', '고품질 크리스탈 사진지', '아름다운 색상', '빠른 건조, 다양한 규격', '12', '4R', '20', '100', '장', '230', 'g/m²'] +[2025-07-03 23:57:02,180] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 1 치환됨 +[2025-07-03 23:57:02,219] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:57:02,254] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8189 사용 중 +[2025-07-03 23:57:02,255] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8189 사용 +[2025-07-03 23:57:14,200] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8189 유휴 +[2025-07-03 23:57:14,203] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + ... + + [[156 192 240] + [159 195 243] + [164 197 246] + ... + [162 202 237] + [161 201 236] + [160 200 235]] + + [[154 191 241] + [156 191 241] + [159 192 242] + ... + [167 207 242] + [163 203 238] + [158 198 233]] + + [[154 191 241] + [152 189 239] + [153 186 236] + ... + [175 215 250] + [168 208 243] + [159 199 234]]] +[2025-07-03 23:57:14,393] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + ... + + [[156 192 240] + [159 195 243] + [164 197 246] + ... + [162 202 237] + [161 201 236] + [160 200 235]] + + [[154 191 241] + [156 191 241] + [159 192 242] + ... + [167 207 242] + [163 203 238] + [158 198 233]] + + [[154 191 241] + [152 189 239] + [153 186 236] + ... + [175 215 250] + [168 208 243] + [159 199 234]]] +[2025-07-03 23:57:14,434] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_seq_img_1.png +[2025-07-03 23:57:14,557] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 1 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_seq_img_1.png +[2025-07-03 23:57:14,683] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 2 처리 시작: D:\py\AutoPercenty3\src\modules\img\2.jpg - OCR+인페인팅 모드 +[2025-07-03 23:57:14,684] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\2.jpg +[2025-07-03 23:57:18,159] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['WA한세유가', '탈수는 세탁기보다 더 건조하다', '진정한 손세탁 면제'] +[2025-07-03 23:57:18,160] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 2 치환됨 +[2025-07-03 23:57:18,192] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:57:18,222] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8189 사용 중 +[2025-07-03 23:57:18,223] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8189 사용 +[2025-07-03 23:57:26,876] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8189 유휴 +[2025-07-03 23:57:26,879] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]]] +[2025-07-03 23:57:26,956] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]]] +[2025-07-03 23:57:27,013] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_seq_img_2.png +[2025-07-03 23:57:27,170] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 2 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_seq_img_2.png +[2025-07-03 23:57:27,299] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 3 처리 시작: D:\py\AutoPercenty3\src\modules\img\3.jpg - OCR+인페인팅 모드 +[2025-07-03 23:57:27,301] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\3.jpg +[2025-07-03 23:57:32,803] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['마이크로 경작기-', '가볍게 당기기만 하면', '3초 만에 빠르게 시작됩니다.', '기계의 연료 공급, 연소, 전송 과정을 3초 만에 빠르게 시작합니다.', '시작하는 데 시간을 낭비하지 않도록 도와드립니다.'] +[2025-07-03 23:57:32,804] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 3 치환됨 +[2025-07-03 23:57:32,832] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:57:32,851] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8189 사용 중 +[2025-07-03 23:57:32,851] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8189 사용 +[2025-07-03 23:57:43,066] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8189 유휴 +[2025-07-03 23:57:43,069] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[ 47 60 58] + [ 50 63 61] + [ 53 66 64] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + [[ 47 60 58] + [ 50 63 61] + [ 53 66 64] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + [[ 47 60 58] + [ 50 63 61] + [ 52 65 63] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + ... + + [[ 41 51 69] + [ 92 102 120] + [103 113 131] + ... + [ 76 85 105] + [ 99 108 128] + [116 125 145]] + + [[ 51 61 79] + [ 82 92 110] + [109 119 137] + ... + [ 79 88 108] + [128 137 157] + [135 144 164]] + + [[ 15 25 43] + [ 9 19 37] + [ 21 31 49] + ... + [ 10 19 39] + [ 29 38 58] + [ 23 32 52]]] +[2025-07-03 23:57:43,153] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[ 47 60 58] + [ 50 63 61] + [ 53 66 64] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + [[ 47 60 58] + [ 50 63 61] + [ 53 66 64] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + [[ 47 60 58] + [ 50 63 61] + [ 52 65 63] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + ... + + [[ 41 51 69] + [ 92 102 120] + [103 113 131] + ... + [ 76 85 105] + [ 99 108 128] + [116 125 145]] + + [[ 51 61 79] + [ 82 92 110] + [109 119 137] + ... + [ 79 88 108] + [128 137 157] + [135 144 164]] + + [[ 15 25 43] + [ 9 19 37] + [ 21 31 49] + ... + [ 10 19 39] + [ 29 38 58] + [ 23 32 52]]] +[2025-07-03 23:57:43,193] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_seq_img_3.png +[2025-07-03 23:57:43,326] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 3 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_seq_img_3.png +[2025-07-03 23:57:43,451] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 4 처리 시작: D:\py\AutoPercenty3\src\modules\img\4.jpg - OCR+인페인팅 모드 +[2025-07-03 23:57:43,453] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\4.jpg +[2025-07-03 23:57:47,856] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['85°C', '185°F', '80℃', '176F', '70°℃', '158F', '60℃', '140°F', '50°C', '122°F', '40C', '104°F', '정확한 온도 조절', '온도 범위는 30℃에서 85℃까지입니다', '30°C', '86°F', '온도 범위는 86°F에서 185°F까지입니다', '정확한 온도 조절'] +[2025-07-03 23:57:47,857] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 4 치환됨 +[2025-07-03 23:57:47,879] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:57:47,893] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8189 사용 중 +[2025-07-03 23:57:47,893] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8189 사용 +[2025-07-03 23:57:54,877] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8189 유휴 +[2025-07-03 23:57:54,880] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [190 185 186]] + + [[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [190 185 186]] + + [[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [189 184 185]] + + ... + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 1 9] + [ 0 1 9]] + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 2 7] + [ 0 2 7]] + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 2 7] + [ 0 2 7]]] +[2025-07-03 23:57:55,025] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [190 185 186]] + + [[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [190 185 186]] + + [[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [189 184 185]] + + ... + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 1 9] + [ 0 1 9]] + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 2 7] + [ 0 2 7]] + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 2 7] + [ 0 2 7]]] +[2025-07-03 23:57:55,047] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_seq_img_4.png +[2025-07-03 23:57:55,128] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 4 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_seq_img_4.png +[2025-07-03 23:57:55,264] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 5 처리 시작: D:\py\AutoPercenty3\src\modules\img\5.jpg - OCR+인페인팅 모드 +[2025-07-03 23:57:55,265] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\5.jpg +[2025-07-03 23:57:59,530] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['현대 미니멀리즘 스타일', '다양한 사용 환경에 더 쉽게 어울립니다', '★환영합니다', '환영합니다', '한정 시간 프로모션, 전 도시 할인', '499원 이상 구매 시 200원 할인 / 999원 이상 구매 시 500원 할인', '동의 간 167'] +[2025-07-03 23:57:59,530] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 5 치환됨 +[2025-07-03 23:57:59,589] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:57:59,630] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8189 사용 중 +[2025-07-03 23:57:59,631] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8189 사용 +[2025-07-03 23:58:15,857] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8189 유휴 +[2025-07-03 23:58:15,861] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[254 253 255] + [254 253 255] + [254 253 255] + ... + [252 254 255] + [252 254 255] + [252 254 255]] + + [[254 253 255] + [254 253 255] + [254 253 255] + ... + [252 254 255] + [252 254 255] + [252 254 255]] + + [[254 253 255] + [254 253 255] + [254 253 255] + ... + [254 254 254] + [254 254 254] + [254 254 254]]] +[2025-07-03 23:58:16,068] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[254 253 255] + [254 253 255] + [254 253 255] + ... + [252 254 255] + [252 254 255] + [252 254 255]] + + [[254 253 255] + [254 253 255] + [254 253 255] + ... + [252 254 255] + [252 254 255] + [252 254 255]] + + [[254 253 255] + [254 253 255] + [254 253 255] + ... + [254 254 254] + [254 254 254] + [254 254 254]]] +[2025-07-03 23:58:16,204] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_seq_img_5.png +[2025-07-03 23:58:16,499] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 5 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_seq_img_5.png +[2025-07-03 23:58:16,634] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 6 처리 시작: D:\py\AutoPercenty3\src\modules\img\6.jpg - OCR+인페인팅 모드 +[2025-07-03 23:58:16,635] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\6.jpg +[2025-07-03 23:58:19,510] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['현대 미니멀리즘 스타일', '다양한 사용 상황에 더 쉽게 어울림', '반원 양끝 디자인', '다양한 즐거움을 담다'] +[2025-07-03 23:58:19,511] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 6 치환됨 +[2025-07-03 23:58:19,571] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:58:19,618] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8189 사용 중 +[2025-07-03 23:58:19,619] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8189 사용 +[2025-07-03 23:58:38,382] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8189 유휴 +[2025-07-03 23:58:38,385] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[ 58 85 112] + [ 56 83 110] + [ 55 82 109] + ... + [137 151 174] + [137 151 174] + [137 151 174]] + + [[ 59 86 113] + [ 56 83 110] + [ 54 81 108] + ... + [137 151 174] + [137 151 174] + [137 151 174]] + + [[ 56 83 110] + [ 56 83 110] + [ 54 81 108] + ... + [137 151 174] + [137 151 174] + [137 151 174]]] +[2025-07-03 23:58:38,526] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[ 58 85 112] + [ 56 83 110] + [ 55 82 109] + ... + [137 151 174] + [137 151 174] + [137 151 174]] + + [[ 59 86 113] + [ 56 83 110] + [ 54 81 108] + ... + [137 151 174] + [137 151 174] + [137 151 174]] + + [[ 56 83 110] + [ 56 83 110] + [ 54 81 108] + ... + [137 151 174] + [137 151 174] + [137 151 174]]] +[2025-07-03 23:58:38,639] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_seq_img_6.png +[2025-07-03 23:58:38,997] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 6 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_seq_img_6.png +[2025-07-03 23:58:39,137] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 7 처리 시작: D:\py\AutoPercenty3\src\modules\img\7.jpg - OCR+인페인팅 모드 +[2025-07-03 23:58:39,139] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\7.jpg +[2025-07-03 23:58:46,379] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['코르노', 'MC', 'PA', 'CNEX', 'CE', 'SGS', 'KORNO', 'CMC 인증', 'CPA 인증', '국가 방폭', 'ISO 인증', 'CE 인증', 'SGS 인증', 'GT-1000', '레이저 분진 측정기', '정밀도 ≤ ±5% F.S', '방호 등급:', ':IP65', '과전압 보호/음성 및 시각 경고/저장 및 인쇄', 'PM0.3/0.5/1.0/2.5/5.0/10um', '다양한 입자 크기의 먼지 입자를 동시에 모니터링 가능', '10만급 이상의 청정실에 적합', '전국', '7일 무조건 반품', '운송 보험료 무료', '무료 배송', '정품/세금계산서 발행 가능/1년 보증'] +[2025-07-03 23:58:46,380] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 7 치환됨 +[2025-07-03 23:58:46,410] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[ 0 0 0 ... 0 0 0] + [ 0 0 0 ... 0 0 0] + [ 0 0 0 ... 0 0 0] + ... + [20 26 43 ... 0 0 0] + [20 26 43 ... 0 0 0] + [20 26 43 ... 0 0 0]] +[2025-07-03 23:58:46,428] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8189 사용 중 +[2025-07-03 23:58:46,430] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8189 사용 +[2025-07-03 23:58:54,310] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8189 유휴 +[2025-07-03 23:58:54,312] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[241 139 50] + [241 138 52] + [241 136 57] + ... + [255 107 60] + [254 109 57] + [254 111 54]] + + [[239 139 51] + [239 138 52] + [239 136 57] + ... + [255 107 60] + [255 110 55] + [254 111 54]] + + [[234 139 52] + [234 138 54] + [236 136 58] + ... + [255 107 60] + [255 110 55] + [255 110 54]] + + ... + + [[ 28 1 0] + [106 69 41] + [232 177 120] + ... + [254 88 99] + [254 88 99] + [254 88 99]] + + [[ 13 2 18] + [ 43 27 28] + [103 73 44] + ... + [254 87 100] + [253 86 99] + [253 86 99]] + + [[ 6 4 34] + [ 6 0 14] + [ 21 2 0] + ... + [253 86 99] + [253 86 99] + [253 86 99]]] +[2025-07-03 23:58:54,512] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[241 139 50] + [241 138 52] + [241 136 57] + ... + [255 107 60] + [254 109 57] + [254 111 54]] + + [[239 139 51] + [239 138 52] + [239 136 57] + ... + [255 107 60] + [255 110 55] + [254 111 54]] + + [[234 139 52] + [234 138 54] + [236 136 58] + ... + [255 107 60] + [255 110 55] + [255 110 54]] + + ... + + [[ 28 1 0] + [106 69 41] + [232 177 120] + ... + [254 88 99] + [254 88 99] + [254 88 99]] + + [[ 13 2 18] + [ 43 27 28] + [103 73 44] + ... + [254 87 100] + [253 86 99] + [253 86 99]] + + [[ 6 4 34] + [ 6 0 14] + [ 21 2 0] + ... + [253 86 99] + [253 86 99] + [253 86 99]]] +[2025-07-03 23:58:54,536] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_seq_img_7.png +[2025-07-03 23:58:54,635] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 7 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_seq_img_7.png +[2025-07-03 23:58:54,660] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 1 처리 시작: D:\py\AutoPercenty3\src\modules\img\1.jpg - OCR+인페인팅 모드 +[2025-07-03 23:58:54,662] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\1.jpg +[2025-07-03 23:58:58,887] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['고품질 크리스탈 사진지', '모든 잉크젯 프린터에 적합', '색상이 선명하고 재현도가 높음', '색상', '고품질 크리스탈 사진지', '아름다운 색상', '빠른 건조, 다양한 규격', '12', '4R', '20', '100', '장', '230', 'g/m²'] +[2025-07-03 23:58:58,888] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 1 치환됨 +[2025-07-03 23:58:58,915] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:58:58,937] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8189 사용 중 +[2025-07-03 23:58:58,937] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8189 사용 +[2025-07-03 23:59:09,504] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8189 유휴 +[2025-07-03 23:59:09,507] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + ... + + [[156 192 240] + [159 195 243] + [164 197 246] + ... + [162 202 237] + [161 201 236] + [160 200 235]] + + [[154 191 241] + [156 191 241] + [159 192 242] + ... + [167 207 242] + [163 203 238] + [158 198 233]] + + [[154 191 241] + [152 189 239] + [153 186 236] + ... + [175 215 250] + [168 208 243] + [159 199 234]]] +[2025-07-03 23:59:09,645] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + [[255 253 253] + [255 253 253] + [255 253 253] + ... + [250 240 240] + [250 240 240] + [250 240 240]] + + ... + + [[156 192 240] + [159 195 243] + [164 197 246] + ... + [162 202 237] + [161 201 236] + [160 200 235]] + + [[154 191 241] + [156 191 241] + [159 192 242] + ... + [167 207 242] + [163 203 238] + [158 198 233]] + + [[154 191 241] + [152 189 239] + [153 186 236] + ... + [175 215 250] + [168 208 243] + [159 199 234]]] +[2025-07-03 23:59:09,694] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_par_img_1.png +[2025-07-03 23:59:09,830] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 1 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_par_img_1.png +[2025-07-03 23:59:09,936] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 2 처리 시작: D:\py\AutoPercenty3\src\modules\img\2.jpg - OCR+인페인팅 모드 +[2025-07-03 23:59:09,937] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\2.jpg +[2025-07-03 23:59:12,644] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['WA한세유가', '탈수는 세탁기보다 더 건조하다', '진정한 손세탁 면제'] +[2025-07-03 23:59:12,645] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 2 치환됨 +[2025-07-03 23:59:12,676] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:59:12,704] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8189 사용 중 +[2025-07-03 23:59:12,705] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8189 사용 +[2025-07-03 23:59:21,309] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8189 유휴 +[2025-07-03 23:59:21,311] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]]] +[2025-07-03 23:59:21,362] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]] + + [[227 125 36] + [227 125 36] + [227 125 36] + ... + [227 125 36] + [227 125 36] + [227 125 36]]] +[2025-07-03 23:59:21,414] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_par_img_2.png +[2025-07-03 23:59:21,571] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 2 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_par_img_2.png +[2025-07-03 23:59:21,573] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 4 처리 시작: D:\py\AutoPercenty3\src\modules\img\4.jpg - OCR+인페인팅 모드 +[2025-07-03 23:59:21,574] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\4.jpg +[2025-07-03 23:59:25,816] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['85°C', '185°F', '80℃', '176F', '70°℃', '158F', '60℃', '140°F', '50°C', '122°F', '40C', '104°F', '정확한 온도 조절', '온도 범위는 30℃에서 85℃까지입니다', '30°C', '86°F', '온도 범위는 86°F에서 185°F까지입니다', '정확한 온도 조절'] +[2025-07-03 23:59:25,817] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 4 치환됨 +[2025-07-03 23:59:25,837] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:59:25,850] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8189 사용 중 +[2025-07-03 23:59:25,851] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8189 사용 +[2025-07-03 23:59:32,966] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8189 유휴 +[2025-07-03 23:59:32,969] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [190 185 186]] + + [[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [190 185 186]] + + [[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [189 184 185]] + + ... + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 1 9] + [ 0 1 9]] + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 2 7] + [ 0 2 7]] + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 2 7] + [ 0 2 7]]] +[2025-07-03 23:59:33,086] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [190 185 186]] + + [[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [190 185 186]] + + [[ 43 39 51] + [ 43 39 51] + [ 43 39 51] + ... + [191 186 187] + [190 185 186] + [189 184 185]] + + ... + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 1 9] + [ 0 1 9]] + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 2 7] + [ 0 2 7]] + + [[ 41 39 51] + [ 41 39 51] + [ 41 39 51] + ... + [ 0 1 9] + [ 0 2 7] + [ 0 2 7]]] +[2025-07-03 23:59:33,110] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_par_img_4.png +[2025-07-03 23:59:33,185] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 4 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_par_img_4.png +[2025-07-03 23:59:33,186] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 7 처리 시작: D:\py\AutoPercenty3\src\modules\img\7.jpg - OCR+인페인팅 모드 +[2025-07-03 23:59:33,187] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\7.jpg +[2025-07-03 23:59:42,445] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['코르노', 'MC', 'PA', 'CNEX', 'CE', 'SGS', 'KORNO', 'CMC 인증', 'CPA 인증', '국가 방폭', 'ISO 인증', 'CE 인증', 'SGS 인증', 'GT-1000', '레이저 분진 측정기', '정확도 ≤ ±5% F.S', '방호 등급:', ':IP65', '과전압 보호/음성광 경고/저장 인쇄', 'PM0.3/0.5/1.0/2.5/5.0/10um', '다양한 입자 크기의 먼지 입자를 동시에 모니터링 가능', '10만 급 이상의 청정실에 적합', '전국', '7일 무조건 반품', '운송 보험료 무료', '무료 배송', '정품/세금계산서 발행 가능/1년 보증'] +[2025-07-03 23:59:42,445] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 7 치환됨 +[2025-07-03 23:59:42,470] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[ 0 0 0 ... 0 0 0] + [ 0 0 0 ... 0 0 0] + [ 0 0 0 ... 0 0 0] + ... + [20 26 43 ... 0 0 0] + [20 26 43 ... 0 0 0] + [20 26 43 ... 0 0 0]] +[2025-07-03 23:59:42,484] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8189 사용 중 +[2025-07-03 23:59:42,485] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8189 사용 +[2025-07-03 23:59:49,935] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8189 유휴 +[2025-07-03 23:59:49,937] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[241 139 50] + [241 138 52] + [241 136 57] + ... + [255 107 60] + [254 109 57] + [254 111 54]] + + [[239 139 51] + [239 138 52] + [239 136 57] + ... + [255 107 60] + [255 110 55] + [254 111 54]] + + [[234 139 52] + [234 138 54] + [236 136 58] + ... + [255 107 60] + [255 110 55] + [255 110 54]] + + ... + + [[ 28 1 0] + [106 69 41] + [232 177 120] + ... + [254 88 99] + [254 88 99] + [254 88 99]] + + [[ 13 2 18] + [ 43 27 28] + [103 73 44] + ... + [254 87 100] + [253 86 99] + [253 86 99]] + + [[ 6 4 34] + [ 6 0 14] + [ 21 2 0] + ... + [253 86 99] + [253 86 99] + [253 86 99]]] +[2025-07-03 23:59:50,151] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[241 139 50] + [241 138 52] + [241 136 57] + ... + [255 107 60] + [254 109 57] + [254 111 54]] + + [[239 139 51] + [239 138 52] + [239 136 57] + ... + [255 107 60] + [255 110 55] + [254 111 54]] + + [[234 139 52] + [234 138 54] + [236 136 58] + ... + [255 107 60] + [255 110 55] + [255 110 54]] + + ... + + [[ 28 1 0] + [106 69 41] + [232 177 120] + ... + [254 88 99] + [254 88 99] + [254 88 99]] + + [[ 13 2 18] + [ 43 27 28] + [103 73 44] + ... + [254 87 100] + [253 86 99] + [253 86 99]] + + [[ 6 4 34] + [ 6 0 14] + [ 21 2 0] + ... + [253 86 99] + [253 86 99] + [253 86 99]]] +[2025-07-03 23:59:50,175] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_par_img_7.png +[2025-07-03 23:59:50,319] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 7 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_par_img_7.png +[2025-07-03 23:59:50,320] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 6 처리 시작: D:\py\AutoPercenty3\src\modules\img\6.jpg - OCR+인페인팅 모드 +[2025-07-03 23:59:50,322] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\6.jpg +[2025-07-03 23:59:53,504] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['현대 미니멀리즘 스타일', '다양한 사용 상황에 더 쉽게 어울림', '반원 양쪽 끝 디자인', '다양한 즐거움을 담다'] +[2025-07-03 23:59:53,505] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 6 치환됨 +[2025-07-03 23:59:53,576] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-03 23:59:53,629] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8189 사용 중 +[2025-07-03 23:59:53,629] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8189 사용 +[2025-07-04 00:00:12,721] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8189 유휴 +[2025-07-04 00:00:12,726] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[ 58 85 112] + [ 56 83 110] + [ 55 82 109] + ... + [137 151 174] + [137 151 174] + [137 151 174]] + + [[ 59 86 113] + [ 56 83 110] + [ 54 81 108] + ... + [137 151 174] + [137 151 174] + [137 151 174]] + + [[ 56 83 110] + [ 56 83 110] + [ 54 81 108] + ... + [137 151 174] + [137 151 174] + [137 151 174]]] +[2025-07-04 00:00:12,892] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[ 58 85 112] + [ 56 83 110] + [ 55 82 109] + ... + [137 151 174] + [137 151 174] + [137 151 174]] + + [[ 59 86 113] + [ 56 83 110] + [ 54 81 108] + ... + [137 151 174] + [137 151 174] + [137 151 174]] + + [[ 56 83 110] + [ 56 83 110] + [ 54 81 108] + ... + [137 151 174] + [137 151 174] + [137 151 174]]] +[2025-07-04 00:00:13,027] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_par_img_6.png +[2025-07-04 00:00:13,390] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 6 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_par_img_6.png +[2025-07-04 00:00:13,392] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 3 처리 시작: D:\py\AutoPercenty3\src\modules\img\3.jpg - OCR+인페인팅 모드 +[2025-07-04 00:00:13,392] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\3.jpg +[2025-07-04 00:00:17,134] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['마이크로 경작기-', '가볍게 당기기만 하면', '3초 만에 빠르게 시작됩니다.', '기계의 연료 공급, 연소, 전송 과정을 3초 만에 빠르게 시작합니다.', '시작하는 데 시간을 낭비하지 않게 해드립니다.'] +[2025-07-04 00:00:17,135] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 3 치환됨 +[2025-07-04 00:00:17,156] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-04 00:00:17,181] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8189 사용 중 +[2025-07-04 00:00:17,182] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8189 사용 +[2025-07-04 00:00:27,283] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8189 유휴 +[2025-07-04 00:00:27,285] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[ 47 60 58] + [ 50 63 61] + [ 53 66 64] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + [[ 47 60 58] + [ 50 63 61] + [ 53 66 64] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + [[ 47 60 58] + [ 50 63 61] + [ 52 65 63] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + ... + + [[ 41 51 69] + [ 92 102 120] + [103 113 131] + ... + [ 76 85 105] + [ 99 108 128] + [116 125 145]] + + [[ 51 61 79] + [ 82 92 110] + [109 119 137] + ... + [ 79 88 108] + [128 137 157] + [135 144 164]] + + [[ 15 25 43] + [ 9 19 37] + [ 21 31 49] + ... + [ 10 19 39] + [ 29 38 58] + [ 23 32 52]]] +[2025-07-04 00:00:27,345] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[ 47 60 58] + [ 50 63 61] + [ 53 66 64] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + [[ 47 60 58] + [ 50 63 61] + [ 53 66 64] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + [[ 47 60 58] + [ 50 63 61] + [ 52 65 63] + ... + [ 32 34 34] + [ 32 34 34] + [ 32 34 34]] + + ... + + [[ 41 51 69] + [ 92 102 120] + [103 113 131] + ... + [ 76 85 105] + [ 99 108 128] + [116 125 145]] + + [[ 51 61 79] + [ 82 92 110] + [109 119 137] + ... + [ 79 88 108] + [128 137 157] + [135 144 164]] + + [[ 15 25 43] + [ 9 19 37] + [ 21 31 49] + ... + [ 10 19 39] + [ 29 38 58] + [ 23 32 52]]] +[2025-07-04 00:00:27,384] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_par_img_3.png +[2025-07-04 00:00:27,514] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 3 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_par_img_3.png +[2025-07-04 00:00:27,515] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 5 처리 시작: D:\py\AutoPercenty3\src\modules\img\5.jpg - OCR+인페인팅 모드 +[2025-07-04 00:00:27,516] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\5.jpg +[2025-07-04 00:00:30,810] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['현대 미니멀 스타일', '다양한 사용 상황에 더 쉽게 어울림', '★환영합니다', '환영합니다', '한정 시간 프로모션, 전 도시 할인', '499원 이상 구매 시 200원 할인 / 999원 이상 구매 시 500원 할인', '동의 간167'] +[2025-07-04 00:00:30,812] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 5 치환됨 +[2025-07-04 00:00:30,865] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료: [[0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + ... + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0] + [0 0 0 ... 0 0 0]] +[2025-07-04 00:00:30,910] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8189 사용 중 +[2025-07-04 00:00:30,911] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8189 사용 +[2025-07-04 00:00:46,882] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8189 유휴 +[2025-07-04 00:00:46,885] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[254 253 255] + [254 253 255] + [254 253 255] + ... + [252 254 255] + [252 254 255] + [252 254 255]] + + [[254 253 255] + [254 253 255] + [254 253 255] + ... + [252 254 255] + [252 254 255] + [252 254 255]] + + [[254 253 255] + [254 253 255] + [254 253 255] + ... + [254 254 254] + [254 254 254] + [254 254 254]]] +[2025-07-04 00:00:47,069] [MainThread] [INFO] [image_processor2.py:process_single_image:166] 텍스트 렌더링 완료: [[[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + [[255 255 255] + [255 255 255] + [255 255 255] + ... + [255 255 255] + [255 255 255] + [255 255 255]] + + ... + + [[254 253 255] + [254 253 255] + [254 253 255] + ... + [252 254 255] + [252 254 255] + [252 254 255]] + + [[254 253 255] + [254 253 255] + [254 253 255] + ... + [252 254 255] + [252 254 255] + [252 254 255]] + + [[254 253 255] + [254 253 255] + [254 253 255] + ... + [254 254 254] + [254 254 254] + [254 254 254]]] +[2025-07-04 00:00:47,172] [MainThread] [INFO] [postImageManager.py:save_image_to_path:57] 이미지 저장 완료 : D:\py\AutoPercenty3\src\modules\output\translated_par_img_5.png +[2025-07-04 00:00:47,470] [MainThread] [INFO] [image_processor2.py:process_single_image:170] 이미지 5 번역 완료: D:\py\AutoPercenty3\src\modules\output\translated_par_img_5.png +[2025-07-04 00:13:37,567] [MainThread] [INFO] [iop_Manager.py:_start_instances:72] IOPaint 인스턴스 1 개 시작 +[2025-07-04 00:13:37,568] [MainThread] [INFO] [iop_Manager.py:_start_instances:76] [8141] 인스턴스 실행 명령: D:\py\AutoPercenty3\src\modules\iop\iop.exe start --model=lama --device=cpu --port 8141 --model-dir D:\py\AutoPercenty3\src\modules\iop\models +[2025-07-04 00:13:45,583] [MainThread] [INFO] [iop_Manager.py:_start_instances:82] [8141] 인스턴스 실행 명시대기: 8초 +[2025-07-04 00:13:45,583] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:51] [8141] 서버 준비 체크 시작 (최대 30초 대기) +[2025-07-04 00:13:49,633] [MainThread] [ERROR] [iop_Manager.py:wait_for_server_ready:66] [8141] 준비 체크 실패 (시도 1회): HTTPConnectionPool(host='localhost', port=8141): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 198, in _new_conn + sock = connection.create_connection( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\connection.py", line 85, in create_connection + raise err + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\connection.py", line 73, in create_connection + sock.connect(sa) +TimeoutError: timed out + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 787, in urlopen + response = self._make_request( + ^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 493, in _make_request + conn.request( + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 494, in request + self.endheaders() + File "D:\Python311\Lib\http\client.py", line 1298, in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + File "D:\Python311\Lib\http\client.py", line 1058, in _send_output + self.send(msg) + File "D:\Python311\Lib\http\client.py", line 996, in send + self.connect() + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 325, in connect + self.sock = self._new_conn() + ^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connection.py", line 207, in _new_conn + raise ConnectTimeoutError( +urllib3.exceptions.ConnectTimeoutError: (, 'Connection to localhost timed out. (connect timeout=2)') + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\Lib\site-packages\requests\adapters.py", line 667, in send + resp = conn.urlopen( + ^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\connectionpool.py", line 841, in urlopen + retries = retries.increment( + ^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\urllib3\util\retry.py", line 519, in increment + raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8141): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\iop_Manager.py", line 56, in wait_for_server_ready + r = requests.get(url, timeout=2) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\api.py", line 73, in get + return request("get", url, params=params, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\api.py", line 59, in request + return session.request(method=method, url=url, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\sessions.py", line 589, in request + resp = self.send(prep, **send_kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\sessions.py", line 703, in send + r = adapter.send(request, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\Lib\site-packages\requests\adapters.py", line 688, in send + raise ConnectTimeout(e, request=request) +requests.exceptions.ConnectTimeout: HTTPConnectionPool(host='localhost', port=8141): Max retries exceeded with url: /api/v1/server-config (Caused by ConnectTimeoutError(, 'Connection to localhost timed out. (connect timeout=2)')) + +[2025-07-04 00:13:52,690] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:57] 응답 : +[2025-07-04 00:13:52,691] [MainThread] [INFO] [iop_Manager.py:wait_for_server_ready:60] [8141] 서버 준비 완료! (시도 2회, 7.1초 소요) +[2025-07-04 00:13:52,692] [MainThread] [INFO] [iop_Manager.py:_start_instances:84] IOPaint 인스턴스 8141 준비됨 +[2025-07-04 00:14:05,131] [MainThread] [INFO] [mask_module.py:__init__:18] 마스크 모듈 초기화 완료 +[2025-07-04 00:14:05,132] [MainThread] [INFO] [text_rendering_module.py:__init__:20] 텍스트 렌더링 모듈 초기화 완료 +[2025-07-04 00:14:05,132] [MainThread] [INFO] [text_rendering_module.py:__init__:21] 기본 폰트: D:\py\AutoPercenty3\src\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-07-04 00:14:05,135] [MainThread] [DEBUG] [postImageManager.py:font_load:40] 폰트 로드 성공: D:\py\AutoPercenty3\src\modules\HakgyoansimDunggeunmisoTTFB.ttf +[2025-07-04 00:14:05,137] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 1 처리 시작: D:\py\AutoPercenty3\src\modules\img\1.jpg - OCR+인페인팅 모드 +[2025-07-04 00:14:05,137] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\1.jpg +[2025-07-04 00:14:05,154] [MainThread] [INFO] [ocr_module.py:detect_text:81] 🔍 OCR 감지 방식: polygon +[2025-07-04 00:14:07,797] [MainThread] [INFO] [ocr_module.py:detect_text:87] ocr_raw_results: [[[[[57.0, 44.0], [708.0, 44.0], [708.0, 122.0], [57.0, 122.0]], ('高质量水晶相纸', 0.9983324408531189)], [[[76.0, 195.0], [373.0, 195.0], [373.0, 225.0], [76.0, 225.0]], ('适合各种喷墨打印机', 0.9938713908195496)], [[[74.0, 263.0], [348.0, 263.0], [348.0, 294.0], [74.0, 294.0]], ('色彩艳丽还原度高', 0.9967608451843262)], [[[401.0, 260.0], [464.0, 260.0], [464.0, 277.0], [401.0, 277.0]], ('Colors', 0.9959214329719543)], [[[529.0, 259.0], [718.0, 259.0], [718.0, 283.0], [529.0, 283.0]], ('高质量水晶相纸', 0.9965680837631226)], [[[397.0, 273.0], [514.0, 271.0], [514.0, 292.0], [397.0, 294.0]], ('Beautiful彩丽', 0.8809211254119873)], [[[72.0, 324.0], [346.0, 324.0], [346.0, 355.0], [72.0, 355.0]], ('打印快干多种规格', 0.9959527254104614)], [[[428.0, 382.0], [441.0, 382.0], [441.0, 389.0], [428.0, 389.0]], ('12', 0.7685988545417786)], [[[452.0, 576.0], [499.0, 576.0], [499.0, 613.0], [452.0, 613.0]], ('4R', 0.9986315965652466)], [[[428.0, 599.0], [442.0, 599.0], [442.0, 614.0], [428.0, 614.0]], ('20', 0.9641289114952087)], [[[428.0, 619.0], [448.0, 619.0], [448.0, 636.0], [428.0, 636.0]], ('100', 0.9982813000679016)], [[[412.0, 640.0], [449.0, 640.0], [449.0, 654.0], [412.0, 654.0]], ('SHEETS', 0.9623525142669678)], [[[463.0, 633.0], [495.0, 633.0], [495.0, 653.0], [463.0, 653.0]], ('230', 0.9994376301765442)], [[[459.0, 654.0], [495.0, 654.0], [495.0, 672.0], [459.0, 672.0]], ('g/m"', 0.7422827482223511)]]] +[2025-07-04 00:14:07,799] [MainThread] [INFO] [ocr_module.py:detect_text:89] line: [[[[57.0, 44.0], [708.0, 44.0], [708.0, 122.0], [57.0, 122.0]], ('高质量水晶相纸', 0.9983324408531189)], [[[76.0, 195.0], [373.0, 195.0], [373.0, 225.0], [76.0, 225.0]], ('适合各种喷墨打印机', 0.9938713908195496)], [[[74.0, 263.0], [348.0, 263.0], [348.0, 294.0], [74.0, 294.0]], ('色彩艳丽还原度高', 0.9967608451843262)], [[[401.0, 260.0], [464.0, 260.0], [464.0, 277.0], [401.0, 277.0]], ('Colors', 0.9959214329719543)], [[[529.0, 259.0], [718.0, 259.0], [718.0, 283.0], [529.0, 283.0]], ('高质量水晶相纸', 0.9965680837631226)], [[[397.0, 273.0], [514.0, 271.0], [514.0, 292.0], [397.0, 294.0]], ('Beautiful彩丽', 0.8809211254119873)], [[[72.0, 324.0], [346.0, 324.0], [346.0, 355.0], [72.0, 355.0]], ('打印快干多种规格', 0.9959527254104614)], [[[428.0, 382.0], [441.0, 382.0], [441.0, 389.0], [428.0, 389.0]], ('12', 0.7685988545417786)], [[[452.0, 576.0], [499.0, 576.0], [499.0, 613.0], [452.0, 613.0]], ('4R', 0.9986315965652466)], [[[428.0, 599.0], [442.0, 599.0], [442.0, 614.0], [428.0, 614.0]], ('20', 0.9641289114952087)], [[[428.0, 619.0], [448.0, 619.0], [448.0, 636.0], [428.0, 636.0]], ('100', 0.9982813000679016)], [[[412.0, 640.0], [449.0, 640.0], [449.0, 654.0], [412.0, 654.0]], ('SHEETS', 0.9623525142669678)], [[[463.0, 633.0], [495.0, 633.0], [495.0, 653.0], [463.0, 653.0]], ('230', 0.9994376301765442)], [[[459.0, 654.0], [495.0, 654.0], [495.0, 672.0], [459.0, 672.0]], ('g/m"', 0.7422827482223511)]] +[2025-07-04 00:14:07,800] [MainThread] [INFO] [ocr_module.py:filter_chinese_text:143] 중국어 텍스트 6개 필터링 완료 +[2025-07-04 00:14:10,933] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['고품질 크리스탈 사진지', '모든 잉크젯 프린터에 적합', '선명한 색상과 높은 재현도', '색상', '고품질 크리스탈 사진지', '아름다운 화려함', '빠른 건조, 다양한 규격', '12', '4R', '20', '100', '장', '230', 'g/m²'] +[2025-07-04 00:14:10,934] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 1 치환됨 +[2025-07-04 00:14:10,961] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료 +[2025-07-04 00:14:10,980] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8141 사용 중 +[2025-07-04 00:14:10,982] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8141 사용 +[2025-07-04 00:14:22,713] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8141 유휴 +[2025-07-04 00:14:22,715] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료 +[2025-07-04 00:14:22,718] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 1 처리 중 오류: 'TextRenderingModule' object has no attribute 'default_font_path' +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\image_processor2.py", line 164, in process_single_image + text_rendered_image = self.text_rendering_module.render_text( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 101, in render_text + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 66, in calculate_optimal_font_size + width, height = self.estimate_text_size(text, size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 54, in estimate_text_size + font = self.get_font(font_size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 38, in get_font + font_path = font_path or self.default_font_path + ^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'TextRenderingModule' object has no attribute 'default_font_path' + +[2025-07-04 00:14:22,833] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 2 처리 시작: D:\py\AutoPercenty3\src\modules\img\2.jpg - OCR+인페인팅 모드 +[2025-07-04 00:14:22,834] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\2.jpg +[2025-07-04 00:14:22,870] [MainThread] [INFO] [ocr_module.py:detect_text:81] 🔍 OCR 감지 방식: polygon +[2025-07-04 00:14:24,313] [MainThread] [INFO] [ocr_module.py:detect_text:87] ocr_raw_results: [[[[[60.0, 60.0], [264.0, 63.0], [264.0, 92.0], [60.0, 89.0]], ('WA汉世刘家', 0.782518208026886)], [[[43.0, 129.0], [541.0, 129.0], [541.0, 182.0], [43.0, 182.0]], ('脱水比洗衣机更干', 0.994388222694397)], [[[38.0, 205.0], [394.0, 203.0], [394.0, 268.0], [39.0, 270.0]], ('真正免手洗', 0.98237144947052)]]] +[2025-07-04 00:14:24,314] [MainThread] [INFO] [ocr_module.py:detect_text:89] line: [[[[60.0, 60.0], [264.0, 63.0], [264.0, 92.0], [60.0, 89.0]], ('WA汉世刘家', 0.782518208026886)], [[[43.0, 129.0], [541.0, 129.0], [541.0, 182.0], [43.0, 182.0]], ('脱水比洗衣机更干', 0.994388222694397)], [[[38.0, 205.0], [394.0, 203.0], [394.0, 268.0], [39.0, 270.0]], ('真正免手洗', 0.98237144947052)]] +[2025-07-04 00:14:24,315] [MainThread] [INFO] [ocr_module.py:filter_chinese_text:143] 중국어 텍스트 3개 필터링 완료 +[2025-07-04 00:14:26,586] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['WA한세유가', '탈수는 세탁기보다 더 건조하다', '진정한 손세탁 면제'] +[2025-07-04 00:14:26,587] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 2 치환됨 +[2025-07-04 00:14:26,620] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료 +[2025-07-04 00:14:26,653] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8141 사용 중 +[2025-07-04 00:14:26,654] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8141 사용 +[2025-07-04 00:14:35,342] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8141 유휴 +[2025-07-04 00:14:35,345] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료 +[2025-07-04 00:14:35,347] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 2 처리 중 오류: 'TextRenderingModule' object has no attribute 'default_font_path' +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\image_processor2.py", line 164, in process_single_image + text_rendered_image = self.text_rendering_module.render_text( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 101, in render_text + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 66, in calculate_optimal_font_size + width, height = self.estimate_text_size(text, size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 54, in estimate_text_size + font = self.get_font(font_size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 38, in get_font + font_path = font_path or self.default_font_path + ^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'TextRenderingModule' object has no attribute 'default_font_path' + +[2025-07-04 00:14:35,455] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 3 처리 시작: D:\py\AutoPercenty3\src\modules\img\3.jpg - OCR+인페인팅 모드 +[2025-07-04 00:14:35,457] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\3.jpg +[2025-07-04 00:14:35,485] [MainThread] [INFO] [ocr_module.py:detect_text:81] 🔍 OCR 감지 방식: polygon +[2025-07-04 00:14:36,993] [MainThread] [INFO] [ocr_module.py:detect_text:87] ocr_raw_results: [[[[[196.0, 55.0], [405.0, 55.0], [405.0, 72.0], [196.0, 72.0]], ('MICROCULTIVATOR-', 0.9893437027931213)], [[[183.0, 102.0], [423.0, 102.0], [423.0, 160.0], [183.0, 160.0]], ('轻轻一拉', 0.9972113370895386)], [[[78.0, 179.0], [525.0, 179.0], [525.0, 227.0], [78.0, 227.0]], ('3秒即可快速启动', 0.996617317199707)], [[[98.0, 266.0], [506.0, 266.0], [506.0, 286.0], [98.0, 286.0]], ('加快机器供油,燃烧,传动流程3秒快速启动', 0.9955319166183472)], [[[178.0, 290.0], [430.0, 290.0], [430.0, 310.0], [178.0, 310.0]], ('让您不用浪费时间在启动上', 0.9967721104621887)]]] +[2025-07-04 00:14:36,994] [MainThread] [INFO] [ocr_module.py:detect_text:89] line: [[[[196.0, 55.0], [405.0, 55.0], [405.0, 72.0], [196.0, 72.0]], ('MICROCULTIVATOR-', 0.9893437027931213)], [[[183.0, 102.0], [423.0, 102.0], [423.0, 160.0], [183.0, 160.0]], ('轻轻一拉', 0.9972113370895386)], [[[78.0, 179.0], [525.0, 179.0], [525.0, 227.0], [78.0, 227.0]], ('3秒即可快速启动', 0.996617317199707)], [[[98.0, 266.0], [506.0, 266.0], [506.0, 286.0], [98.0, 286.0]], ('加快机器供油,燃烧,传动流程3秒快速启动', 0.9955319166183472)], [[[178.0, 290.0], [430.0, 290.0], [430.0, 310.0], [178.0, 310.0]], ('让您不用浪费时间在启动上', 0.9967721104621887)]] +[2025-07-04 00:14:36,996] [MainThread] [INFO] [ocr_module.py:filter_chinese_text:143] 중국어 텍스트 4개 필터링 완료 +[2025-07-04 00:14:39,899] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['마이크로 경작기-', '가볍게 한 번 당기면', '3초 만에 빠르게 시작됩니다.', '기계의 연료 공급, 연소, 전송 과정을 3초 만에 빠르게 시작합니다.', '시작하는 데 시간을 낭비하지 않도록 도와드립니다.'] +[2025-07-04 00:14:39,900] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 3 치환됨 +[2025-07-04 00:14:39,925] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료 +[2025-07-04 00:14:39,946] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8141 사용 중 +[2025-07-04 00:14:39,946] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8141 사용 +[2025-07-04 00:14:50,498] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8141 유휴 +[2025-07-04 00:14:50,501] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료 +[2025-07-04 00:14:50,503] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 3 처리 중 오류: 'TextRenderingModule' object has no attribute 'default_font_path' +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\image_processor2.py", line 164, in process_single_image + text_rendered_image = self.text_rendering_module.render_text( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 101, in render_text + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 66, in calculate_optimal_font_size + width, height = self.estimate_text_size(text, size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 54, in estimate_text_size + font = self.get_font(font_size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 38, in get_font + font_path = font_path or self.default_font_path + ^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'TextRenderingModule' object has no attribute 'default_font_path' + +[2025-07-04 00:14:50,614] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 4 처리 시작: D:\py\AutoPercenty3\src\modules\img\4.jpg - OCR+인페인팅 모드 +[2025-07-04 00:14:50,615] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\4.jpg +[2025-07-04 00:14:50,625] [MainThread] [INFO] [ocr_module.py:detect_text:81] 🔍 OCR 감지 방식: polygon +[2025-07-04 00:14:52,920] [MainThread] [INFO] [ocr_module.py:detect_text:87] ocr_raw_results: [[[[[431.0, 71.0], [467.0, 71.0], [467.0, 86.0], [431.0, 86.0]], ('85°C', 0.8005948662757874)], [[[516.0, 69.0], [558.0, 69.0], [558.0, 88.0], [516.0, 88.0]], ('185°F', 0.9477065801620483)], [[[430.0, 138.0], [469.0, 138.0], [469.0, 156.0], [430.0, 156.0]], ('80℃', 0.948918879032135)], [[[510.0, 138.0], [560.0, 136.0], [561.0, 154.0], [511.0, 156.0]], ('176F', 0.9774500727653503)], [[[431.0, 204.0], [487.0, 208.0], [486.0, 226.0], [430.0, 223.0]], ('70°℃', 0.8445274829864502)], [[[492.0, 208.0], [559.0, 204.0], [561.0, 222.0], [494.0, 226.0]], ('158F', 0.9599380493164062)], [[[430.0, 274.0], [469.0, 274.0], [469.0, 292.0], [430.0, 292.0]], ('60℃', 0.959047794342041)], [[[508.0, 275.0], [560.0, 275.0], [560.0, 292.0], [508.0, 292.0]], ('140°F', 0.9507576823234558)], [[[431.0, 344.0], [467.0, 344.0], [467.0, 359.0], [431.0, 359.0]], ('50°C', 0.8583885431289673)], [[[514.0, 342.0], [560.0, 342.0], [560.0, 360.0], [514.0, 360.0]], ('122°F', 0.8599249720573425)], [[[429.0, 412.0], [467.0, 409.0], [468.0, 428.0], [430.0, 431.0]], ('40C', 0.8281779885292053)], [[[515.0, 412.0], [559.0, 412.0], [559.0, 427.0], [515.0, 427.0]], ('104°F', 0.9546159505844116)], [[[24.0, 447.0], [227.0, 449.0], [226.0, 477.0], [24.0, 475.0]], ('精确的温度控制', 0.9948644042015076)], [[[25.0, 484.0], [262.0, 484.0], [262.0, 504.0], [25.0, 504.0]], ('温度范围从30℃至85℃', 0.9833672642707825)], [[[430.0, 480.0], [468.0, 480.0], [468.0, 497.0], [430.0, 497.0]], ('30°C', 0.7743486762046814)], [[[513.0, 480.0], [549.0, 480.0], [549.0, 497.0], [513.0, 497.0]], ('86°F', 0.9255594611167908)], [[[21.0, 514.0], [428.0, 512.0], [428.0, 536.0], [21.0, 538.0]], ('Temperature range from 86 F to 185°F', 0.9300384521484375)], [[[25.0, 542.0], [572.0, 543.0], [572.0, 570.0], [25.0, 569.0]], ('PRECISE TEMPERATURECONTROL', 0.9752708673477173)]]] +[2025-07-04 00:14:52,921] [MainThread] [INFO] [ocr_module.py:detect_text:89] line: [[[[431.0, 71.0], [467.0, 71.0], [467.0, 86.0], [431.0, 86.0]], ('85°C', 0.8005948662757874)], [[[516.0, 69.0], [558.0, 69.0], [558.0, 88.0], [516.0, 88.0]], ('185°F', 0.9477065801620483)], [[[430.0, 138.0], [469.0, 138.0], [469.0, 156.0], [430.0, 156.0]], ('80℃', 0.948918879032135)], [[[510.0, 138.0], [560.0, 136.0], [561.0, 154.0], [511.0, 156.0]], ('176F', 0.9774500727653503)], [[[431.0, 204.0], [487.0, 208.0], [486.0, 226.0], [430.0, 223.0]], ('70°℃', 0.8445274829864502)], [[[492.0, 208.0], [559.0, 204.0], [561.0, 222.0], [494.0, 226.0]], ('158F', 0.9599380493164062)], [[[430.0, 274.0], [469.0, 274.0], [469.0, 292.0], [430.0, 292.0]], ('60℃', 0.959047794342041)], [[[508.0, 275.0], [560.0, 275.0], [560.0, 292.0], [508.0, 292.0]], ('140°F', 0.9507576823234558)], [[[431.0, 344.0], [467.0, 344.0], [467.0, 359.0], [431.0, 359.0]], ('50°C', 0.8583885431289673)], [[[514.0, 342.0], [560.0, 342.0], [560.0, 360.0], [514.0, 360.0]], ('122°F', 0.8599249720573425)], [[[429.0, 412.0], [467.0, 409.0], [468.0, 428.0], [430.0, 431.0]], ('40C', 0.8281779885292053)], [[[515.0, 412.0], [559.0, 412.0], [559.0, 427.0], [515.0, 427.0]], ('104°F', 0.9546159505844116)], [[[24.0, 447.0], [227.0, 449.0], [226.0, 477.0], [24.0, 475.0]], ('精确的温度控制', 0.9948644042015076)], [[[25.0, 484.0], [262.0, 484.0], [262.0, 504.0], [25.0, 504.0]], ('温度范围从30℃至85℃', 0.9833672642707825)], [[[430.0, 480.0], [468.0, 480.0], [468.0, 497.0], [430.0, 497.0]], ('30°C', 0.7743486762046814)], [[[513.0, 480.0], [549.0, 480.0], [549.0, 497.0], [513.0, 497.0]], ('86°F', 0.9255594611167908)], [[[21.0, 514.0], [428.0, 512.0], [428.0, 536.0], [21.0, 538.0]], ('Temperature range from 86 F to 185°F', 0.9300384521484375)], [[[25.0, 542.0], [572.0, 543.0], [572.0, 570.0], [25.0, 569.0]], ('PRECISE TEMPERATURECONTROL', 0.9752708673477173)]] +[2025-07-04 00:14:52,923] [MainThread] [INFO] [ocr_module.py:filter_chinese_text:143] 중국어 텍스트 2개 필터링 완료 +[2025-07-04 00:14:56,081] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['85°C', '185°F', '80℃', '176F', '70°℃', '158F', '60℃', '140°F', '50°C', '122°F', '40C', '104°F', '정확한 온도 조절', '온도 범위는 30℃에서 85℃까지입니다', '30°C', '86°F', '온도 범위는 86°F에서 185°F까지입니다', '정확한 온도 조절'] +[2025-07-04 00:14:56,082] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 4 치환됨 +[2025-07-04 00:14:56,105] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료 +[2025-07-04 00:14:56,120] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8141 사용 중 +[2025-07-04 00:14:56,121] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8141 사용 +[2025-07-04 00:15:03,273] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8141 유휴 +[2025-07-04 00:15:03,274] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료 +[2025-07-04 00:15:03,278] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 4 처리 중 오류: 'TextRenderingModule' object has no attribute 'default_font_path' +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\image_processor2.py", line 164, in process_single_image + text_rendered_image = self.text_rendering_module.render_text( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 101, in render_text + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 66, in calculate_optimal_font_size + width, height = self.estimate_text_size(text, size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 54, in estimate_text_size + font = self.get_font(font_size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 38, in get_font + font_path = font_path or self.default_font_path + ^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'TextRenderingModule' object has no attribute 'default_font_path' + +[2025-07-04 00:15:03,390] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 5 처리 시작: D:\py\AutoPercenty3\src\modules\img\5.jpg - OCR+인페인팅 모드 +[2025-07-04 00:15:03,391] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\5.jpg +[2025-07-04 00:15:03,440] [MainThread] [INFO] [ocr_module.py:detect_text:81] 🔍 OCR 감지 방식: polygon +[2025-07-04 00:15:06,173] [MainThread] [INFO] [ocr_module.py:detect_text:87] ocr_raw_results: [[[[[339.0, 103.0], [855.0, 103.0], [855.0, 182.0], [339.0, 182.0]], ('现代极简风格', 0.996478259563446)], [[[235.0, 219.0], [963.0, 219.0], [963.0, 279.0], [235.0, 279.0]], ('更易搭配各种使用场景', 0.9974321126937866)], [[[478.0, 727.0], [649.0, 727.0], [649.0, 762.0], [478.0, 762.0]], ('★WELCOME', 0.8576037883758545)], [[[407.0, 760.0], [724.0, 760.0], [724.0, 855.0], [407.0, 855.0]], ('欢迎光临', 0.9999475479125977)], [[[473.0, 856.0], [624.0, 863.0], [623.0, 898.0], [471.0, 891.0]], ('限时促销礼惠全城', 0.9319560527801514)], [[[446.0, 947.0], [640.0, 962.0], [638.0, 995.0], [443.0, 980.0]], ('满499减200/满999减500', 0.9274235963821411)], [[[481.0, 980.0], [595.0, 991.0], [592.0, 1016.0], [479.0, 1005.0]], ('动的间167', 0.7949853539466858)]]] +[2025-07-04 00:15:06,174] [MainThread] [INFO] [ocr_module.py:detect_text:89] line: [[[[339.0, 103.0], [855.0, 103.0], [855.0, 182.0], [339.0, 182.0]], ('现代极简风格', 0.996478259563446)], [[[235.0, 219.0], [963.0, 219.0], [963.0, 279.0], [235.0, 279.0]], ('更易搭配各种使用场景', 0.9974321126937866)], [[[478.0, 727.0], [649.0, 727.0], [649.0, 762.0], [478.0, 762.0]], ('★WELCOME', 0.8576037883758545)], [[[407.0, 760.0], [724.0, 760.0], [724.0, 855.0], [407.0, 855.0]], ('欢迎光临', 0.9999475479125977)], [[[473.0, 856.0], [624.0, 863.0], [623.0, 898.0], [471.0, 891.0]], ('限时促销礼惠全城', 0.9319560527801514)], [[[446.0, 947.0], [640.0, 962.0], [638.0, 995.0], [443.0, 980.0]], ('满499减200/满999减500', 0.9274235963821411)], [[[481.0, 980.0], [595.0, 991.0], [592.0, 1016.0], [479.0, 1005.0]], ('动的间167', 0.7949853539466858)]] +[2025-07-04 00:15:06,177] [MainThread] [INFO] [ocr_module.py:filter_chinese_text:143] 중국어 텍스트 6개 필터링 완료 +[2025-07-04 00:15:08,061] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['현대 미니멀리즘 스타일', '다양한 사용 상황에 더 쉽게 어울립니다', '★환영합니다', '환영합니다', '한정 시간 프로모션, 전 도시 할인', '499원 이상 구매 시 200원 할인 / 999원 이상 구매 시 500원 할인', '동의 간 167'] +[2025-07-04 00:15:08,061] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 5 치환됨 +[2025-07-04 00:15:08,132] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료 +[2025-07-04 00:15:08,175] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8141 사용 중 +[2025-07-04 00:15:08,176] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8141 사용 +[2025-07-04 00:15:25,241] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8141 유휴 +[2025-07-04 00:15:25,246] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료 +[2025-07-04 00:15:25,257] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 5 처리 중 오류: 'TextRenderingModule' object has no attribute 'default_font_path' +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\image_processor2.py", line 164, in process_single_image + text_rendered_image = self.text_rendering_module.render_text( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 101, in render_text + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 66, in calculate_optimal_font_size + width, height = self.estimate_text_size(text, size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 54, in estimate_text_size + font = self.get_font(font_size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 38, in get_font + font_path = font_path or self.default_font_path + ^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'TextRenderingModule' object has no attribute 'default_font_path' + +[2025-07-04 00:15:25,376] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 6 처리 시작: D:\py\AutoPercenty3\src\modules\img\6.jpg - OCR+인페인팅 모드 +[2025-07-04 00:15:25,379] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\6.jpg +[2025-07-04 00:15:25,424] [MainThread] [INFO] [ocr_module.py:detect_text:81] 🔍 OCR 감지 방식: polygon +[2025-07-04 00:15:26,885] [MainThread] [INFO] [ocr_module.py:detect_text:87] ocr_raw_results: [[[[[344.0, 108.0], [855.0, 108.0], [855.0, 182.0], [344.0, 182.0]], ('现代极简风格', 0.9951332211494446)], [[[235.0, 219.0], [964.0, 219.0], [964.0, 279.0], [235.0, 279.0]], ('更易搭配各种使用场景', 0.9972091913223267)], [[[136.0, 447.0], [717.0, 447.0], [717.0, 534.0], [136.0, 534.0]], ('半圆两端设计', 0.9981253743171692)], [[[134.0, 571.0], [715.0, 571.0], [715.0, 658.0], [134.0, 658.0]], ('承载各种欢乐', 0.9950291514396667)]]] +[2025-07-04 00:15:26,886] [MainThread] [INFO] [ocr_module.py:detect_text:89] line: [[[[344.0, 108.0], [855.0, 108.0], [855.0, 182.0], [344.0, 182.0]], ('现代极简风格', 0.9951332211494446)], [[[235.0, 219.0], [964.0, 219.0], [964.0, 279.0], [235.0, 279.0]], ('更易搭配各种使用场景', 0.9972091913223267)], [[[136.0, 447.0], [717.0, 447.0], [717.0, 534.0], [136.0, 534.0]], ('半圆两端设计', 0.9981253743171692)], [[[134.0, 571.0], [715.0, 571.0], [715.0, 658.0], [134.0, 658.0]], ('承载各种欢乐', 0.9950291514396667)]] +[2025-07-04 00:15:26,890] [MainThread] [INFO] [ocr_module.py:filter_chinese_text:143] 중국어 텍스트 4개 필터링 완료 +[2025-07-04 00:15:28,642] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['현대 미니멀리즘 스타일', '다양한 사용 환경에 더 쉽게 어울림', '반원 양끝 디자인', '다양한 즐거움을 담다'] +[2025-07-04 00:15:28,643] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 6 치환됨 +[2025-07-04 00:15:28,702] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료 +[2025-07-04 00:15:28,756] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8141 사용 중 +[2025-07-04 00:15:28,757] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8141 사용 +[2025-07-04 00:15:49,644] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8141 유휴 +[2025-07-04 00:15:49,647] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료 +[2025-07-04 00:15:49,652] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 6 처리 중 오류: 'TextRenderingModule' object has no attribute 'default_font_path' +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\image_processor2.py", line 164, in process_single_image + text_rendered_image = self.text_rendering_module.render_text( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 101, in render_text + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 66, in calculate_optimal_font_size + width, height = self.estimate_text_size(text, size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 54, in estimate_text_size + font = self.get_font(font_size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 38, in get_font + font_path = font_path or self.default_font_path + ^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'TextRenderingModule' object has no attribute 'default_font_path' + +[2025-07-04 00:15:49,774] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 7 처리 시작: D:\py\AutoPercenty3\src\modules\img\7.jpg - OCR+인페인팅 모드 +[2025-07-04 00:15:49,776] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\7.jpg +[2025-07-04 00:15:49,789] [MainThread] [INFO] [ocr_module.py:detect_text:81] 🔍 OCR 감지 방식: polygon +[2025-07-04 00:15:52,421] [MainThread] [INFO] [ocr_module.py:detect_text:87] ocr_raw_results: [[[[[74.0, 20.0], [157.0, 20.0], [157.0, 49.0], [74.0, 49.0]], ('科尔诺', 0.9947752952575684)], [[[243.0, 22.0], [291.0, 22.0], [291.0, 45.0], [243.0, 45.0]], ('MC', 0.6195889711380005)], [[[309.0, 22.0], [350.0, 24.0], [349.0, 43.0], [308.0, 41.0]], ('PA', 0.9959157705307007)], [[[372.0, 25.0], [423.0, 25.0], [423.0, 41.0], [372.0, 41.0]], ('CNEX', 0.9921520352363586)], [[[493.0, 17.0], [542.0, 17.0], [542.0, 48.0], [493.0, 48.0]], ('CE', 0.9579372406005859)], [[[561.0, 19.0], [611.0, 19.0], [611.0, 46.0], [561.0, 46.0]], ('SGS', 0.9938836693763733)], [[[75.0, 53.0], [158.0, 53.0], [158.0, 74.0], [75.0, 74.0]], ('KORNO', 0.9965262413024902)], [[[240.0, 57.0], [291.0, 57.0], [291.0, 71.0], [240.0, 71.0]], ('CMC认证', 0.9946392178535461)], [[[306.0, 57.0], [355.0, 57.0], [355.0, 71.0], [306.0, 71.0]], ('CPA认证', 0.9976503252983093)], [[[371.0, 57.0], [419.0, 57.0], [419.0, 71.0], [371.0, 71.0]], ('国家防爆', 0.9972963333129883)], [[[436.0, 57.0], [480.0, 57.0], [480.0, 71.0], [436.0, 71.0]], ('ISO认证', 0.996651291847229)], [[[500.0, 57.0], [539.0, 57.0], [539.0, 71.0], [500.0, 71.0]], ('CE认证', 0.9982097148895264)], [[[562.0, 57.0], [609.0, 57.0], [609.0, 71.0], [562.0, 71.0]], ('SGS认证', 0.9988522529602051)], [[[45.0, 101.0], [283.0, 101.0], [283.0, 146.0], [45.0, 146.0]], ('GT-1000', 0.9948582053184509)], [[[45.0, 166.0], [407.0, 166.0], [407.0, 211.0], [45.0, 211.0]], ('激光粉尘检测仪', 0.9980788230895996)], [[[29.0, 239.0], [216.0, 237.0], [216.0, 264.0], [29.0, 266.0]], ('精度≤±5%F.S', 0.9504798054695129)], [[[28.0, 297.0], [162.0, 297.0], [162.0, 324.0], [28.0, 324.0]], ('防护等级:', 0.9984332323074341)], [[[148.0, 295.0], [236.0, 295.0], [236.0, 323.0], [148.0, 323.0]], (':IP65', 0.9789118766784668)], [[[23.0, 356.0], [420.0, 356.0], [420.0, 380.0], [23.0, 380.0]], ('过压保护/声光报警/存储打印', 0.9622747302055359)], [[[21.0, 410.0], [425.0, 413.0], [425.0, 440.0], [21.0, 437.0]], ('PM0.3/0.5/1.0/2.5/5.0/10um', 0.9829097986221313)], [[[29.0, 469.0], [371.0, 469.0], [371.0, 492.0], [29.0, 492.0]], ('可同时监测多种粒径尘埃粒子数', 0.9967579245567322)], [[[29.0, 505.0], [271.0, 505.0], [271.0, 526.0], [29.0, 526.0]], ('适合十万级以上洁净室', 0.9917163848876953)], [[[15.0, 532.0], [119.0, 532.0], [119.0, 589.0], [15.0, 589.0]], ('全国', 0.9994618892669678)], [[[194.0, 544.0], [391.0, 544.0], [391.0, 571.0], [194.0, 571.0]], ('7天无理由退货', 0.9978777766227722)], [[[428.0, 543.0], [542.0, 543.0], [542.0, 571.0], [428.0, 571.0]], ('赠运险费', 0.998300313949585)], [[[15.0, 585.0], [122.0, 587.0], [121.0, 639.0], [14.0, 637.0]], ('包邮', 0.9907819032669067)], [[[138.0, 585.0], [625.0, 583.0], [625.0, 617.0], [138.0, 619.0]], ('原厂正品/可开发票/质保一年', 0.9910593032836914)]]] +[2025-07-04 00:15:52,433] [MainThread] [INFO] [ocr_module.py:detect_text:89] line: [[[[74.0, 20.0], [157.0, 20.0], [157.0, 49.0], [74.0, 49.0]], ('科尔诺', 0.9947752952575684)], [[[243.0, 22.0], [291.0, 22.0], [291.0, 45.0], [243.0, 45.0]], ('MC', 0.6195889711380005)], [[[309.0, 22.0], [350.0, 24.0], [349.0, 43.0], [308.0, 41.0]], ('PA', 0.9959157705307007)], [[[372.0, 25.0], [423.0, 25.0], [423.0, 41.0], [372.0, 41.0]], ('CNEX', 0.9921520352363586)], [[[493.0, 17.0], [542.0, 17.0], [542.0, 48.0], [493.0, 48.0]], ('CE', 0.9579372406005859)], [[[561.0, 19.0], [611.0, 19.0], [611.0, 46.0], [561.0, 46.0]], ('SGS', 0.9938836693763733)], [[[75.0, 53.0], [158.0, 53.0], [158.0, 74.0], [75.0, 74.0]], ('KORNO', 0.9965262413024902)], [[[240.0, 57.0], [291.0, 57.0], [291.0, 71.0], [240.0, 71.0]], ('CMC认证', 0.9946392178535461)], [[[306.0, 57.0], [355.0, 57.0], [355.0, 71.0], [306.0, 71.0]], ('CPA认证', 0.9976503252983093)], [[[371.0, 57.0], [419.0, 57.0], [419.0, 71.0], [371.0, 71.0]], ('国家防爆', 0.9972963333129883)], [[[436.0, 57.0], [480.0, 57.0], [480.0, 71.0], [436.0, 71.0]], ('ISO认证', 0.996651291847229)], [[[500.0, 57.0], [539.0, 57.0], [539.0, 71.0], [500.0, 71.0]], ('CE认证', 0.9982097148895264)], [[[562.0, 57.0], [609.0, 57.0], [609.0, 71.0], [562.0, 71.0]], ('SGS认证', 0.9988522529602051)], [[[45.0, 101.0], [283.0, 101.0], [283.0, 146.0], [45.0, 146.0]], ('GT-1000', 0.9948582053184509)], [[[45.0, 166.0], [407.0, 166.0], [407.0, 211.0], [45.0, 211.0]], ('激光粉尘检测仪', 0.9980788230895996)], [[[29.0, 239.0], [216.0, 237.0], [216.0, 264.0], [29.0, 266.0]], ('精度≤±5%F.S', 0.9504798054695129)], [[[28.0, 297.0], [162.0, 297.0], [162.0, 324.0], [28.0, 324.0]], ('防护等级:', 0.9984332323074341)], [[[148.0, 295.0], [236.0, 295.0], [236.0, 323.0], [148.0, 323.0]], (':IP65', 0.9789118766784668)], [[[23.0, 356.0], [420.0, 356.0], [420.0, 380.0], [23.0, 380.0]], ('过压保护/声光报警/存储打印', 0.9622747302055359)], [[[21.0, 410.0], [425.0, 413.0], [425.0, 440.0], [21.0, 437.0]], ('PM0.3/0.5/1.0/2.5/5.0/10um', 0.9829097986221313)], [[[29.0, 469.0], [371.0, 469.0], [371.0, 492.0], [29.0, 492.0]], ('可同时监测多种粒径尘埃粒子数', 0.9967579245567322)], [[[29.0, 505.0], [271.0, 505.0], [271.0, 526.0], [29.0, 526.0]], ('适合十万级以上洁净室', 0.9917163848876953)], [[[15.0, 532.0], [119.0, 532.0], [119.0, 589.0], [15.0, 589.0]], ('全国', 0.9994618892669678)], [[[194.0, 544.0], [391.0, 544.0], [391.0, 571.0], [194.0, 571.0]], ('7天无理由退货', 0.9978777766227722)], [[[428.0, 543.0], [542.0, 543.0], [542.0, 571.0], [428.0, 571.0]], ('赠运险费', 0.998300313949585)], [[[15.0, 585.0], [122.0, 587.0], [121.0, 639.0], [14.0, 637.0]], ('包邮', 0.9907819032669067)], [[[138.0, 585.0], [625.0, 583.0], [625.0, 617.0], [138.0, 619.0]], ('原厂正品/可开发票/质保一年', 0.9910593032836914)]] +[2025-07-04 00:15:52,436] [MainThread] [INFO] [ocr_module.py:filter_chinese_text:143] 중국어 텍스트 18개 필터링 완료 +[2025-07-04 00:15:57,300] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['코르노', 'MC', 'PA', 'CNEX', 'CE', 'SGS', '코르노', 'CMC 인증', 'CPA 인증', '국가 방폭', 'ISO 인증', 'CE 인증', 'SGS 인증', 'GT-1000', '레이저 분진 측정기', '정밀도 ≤ ±5% F.S', '방호 등급:', ':IP65', '과전압 보호/음성 및 빛 경고/저장 및 인쇄', 'PM0.3/0.5/1.0/2.5/5.0/10um', '다양한 입자 크기의 먼지 입자를 동시에 모니터링 가능', '10만급 이상의 청정실에 적합', '전국', '7일 무조건 반품', '운송 보험료 무료', '무료 배송', '정품/세금계산서 발행 가능/1년 보증'] +[2025-07-04 00:15:57,301] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 7 치환됨 +[2025-07-04 00:15:57,333] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료 +[2025-07-04 00:15:57,353] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8141 사용 중 +[2025-07-04 00:15:57,355] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8141 사용 +[2025-07-04 00:16:05,879] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8141 유휴 +[2025-07-04 00:16:05,880] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료 +[2025-07-04 00:16:05,883] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 7 처리 중 오류: 'TextRenderingModule' object has no attribute 'default_font_path' +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\image_processor2.py", line 164, in process_single_image + text_rendered_image = self.text_rendering_module.render_text( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 101, in render_text + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 66, in calculate_optimal_font_size + width, height = self.estimate_text_size(text, size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 54, in estimate_text_size + font = self.get_font(font_size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 38, in get_font + font_path = font_path or self.default_font_path + ^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'TextRenderingModule' object has no attribute 'default_font_path' + +[2025-07-04 00:16:05,892] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 1 처리 시작: D:\py\AutoPercenty3\src\modules\img\1.jpg - OCR+인페인팅 모드 +[2025-07-04 00:16:05,893] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\1.jpg +[2025-07-04 00:16:05,913] [MainThread] [INFO] [ocr_module.py:detect_text:81] 🔍 OCR 감지 방식: polygon +[2025-07-04 00:16:09,115] [MainThread] [INFO] [ocr_module.py:detect_text:87] ocr_raw_results: [[[[[57.0, 44.0], [708.0, 44.0], [708.0, 122.0], [57.0, 122.0]], ('高质量水晶相纸', 0.9983324408531189)], [[[76.0, 195.0], [373.0, 195.0], [373.0, 225.0], [76.0, 225.0]], ('适合各种喷墨打印机', 0.9938713908195496)], [[[74.0, 263.0], [348.0, 263.0], [348.0, 294.0], [74.0, 294.0]], ('色彩艳丽还原度高', 0.9967608451843262)], [[[401.0, 260.0], [464.0, 260.0], [464.0, 277.0], [401.0, 277.0]], ('Colors', 0.9959214329719543)], [[[529.0, 259.0], [718.0, 259.0], [718.0, 283.0], [529.0, 283.0]], ('高质量水晶相纸', 0.9965680837631226)], [[[397.0, 273.0], [514.0, 271.0], [514.0, 292.0], [397.0, 294.0]], ('Beautiful彩丽', 0.8809211254119873)], [[[72.0, 324.0], [346.0, 324.0], [346.0, 355.0], [72.0, 355.0]], ('打印快干多种规格', 0.9959527254104614)], [[[428.0, 382.0], [441.0, 382.0], [441.0, 389.0], [428.0, 389.0]], ('12', 0.7685988545417786)], [[[452.0, 576.0], [499.0, 576.0], [499.0, 613.0], [452.0, 613.0]], ('4R', 0.9986315965652466)], [[[428.0, 599.0], [442.0, 599.0], [442.0, 614.0], [428.0, 614.0]], ('20', 0.9641289114952087)], [[[428.0, 619.0], [448.0, 619.0], [448.0, 636.0], [428.0, 636.0]], ('100', 0.9982813000679016)], [[[412.0, 640.0], [449.0, 640.0], [449.0, 654.0], [412.0, 654.0]], ('SHEETS', 0.9623525142669678)], [[[463.0, 633.0], [495.0, 633.0], [495.0, 653.0], [463.0, 653.0]], ('230', 0.9994376301765442)], [[[459.0, 654.0], [495.0, 654.0], [495.0, 672.0], [459.0, 672.0]], ('g/m"', 0.7422827482223511)]]] +[2025-07-04 00:16:09,117] [MainThread] [INFO] [ocr_module.py:detect_text:89] line: [[[[57.0, 44.0], [708.0, 44.0], [708.0, 122.0], [57.0, 122.0]], ('高质量水晶相纸', 0.9983324408531189)], [[[76.0, 195.0], [373.0, 195.0], [373.0, 225.0], [76.0, 225.0]], ('适合各种喷墨打印机', 0.9938713908195496)], [[[74.0, 263.0], [348.0, 263.0], [348.0, 294.0], [74.0, 294.0]], ('色彩艳丽还原度高', 0.9967608451843262)], [[[401.0, 260.0], [464.0, 260.0], [464.0, 277.0], [401.0, 277.0]], ('Colors', 0.9959214329719543)], [[[529.0, 259.0], [718.0, 259.0], [718.0, 283.0], [529.0, 283.0]], ('高质量水晶相纸', 0.9965680837631226)], [[[397.0, 273.0], [514.0, 271.0], [514.0, 292.0], [397.0, 294.0]], ('Beautiful彩丽', 0.8809211254119873)], [[[72.0, 324.0], [346.0, 324.0], [346.0, 355.0], [72.0, 355.0]], ('打印快干多种规格', 0.9959527254104614)], [[[428.0, 382.0], [441.0, 382.0], [441.0, 389.0], [428.0, 389.0]], ('12', 0.7685988545417786)], [[[452.0, 576.0], [499.0, 576.0], [499.0, 613.0], [452.0, 613.0]], ('4R', 0.9986315965652466)], [[[428.0, 599.0], [442.0, 599.0], [442.0, 614.0], [428.0, 614.0]], ('20', 0.9641289114952087)], [[[428.0, 619.0], [448.0, 619.0], [448.0, 636.0], [428.0, 636.0]], ('100', 0.9982813000679016)], [[[412.0, 640.0], [449.0, 640.0], [449.0, 654.0], [412.0, 654.0]], ('SHEETS', 0.9623525142669678)], [[[463.0, 633.0], [495.0, 633.0], [495.0, 653.0], [463.0, 653.0]], ('230', 0.9994376301765442)], [[[459.0, 654.0], [495.0, 654.0], [495.0, 672.0], [459.0, 672.0]], ('g/m"', 0.7422827482223511)]] +[2025-07-04 00:16:09,119] [MainThread] [INFO] [ocr_module.py:filter_chinese_text:143] 중국어 텍스트 6개 필터링 완료 +[2025-07-04 00:16:11,651] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['고품질 크리스탈 사진지', '모든 잉크젯 프린터에 적합', '색상이 선명하고 재현도가 높음', '색상', '고품질 크리스탈 사진지', '아름다운 색상', '빠른 건조, 다양한 규격', '12', '4R', '20', '100', '장', '230', 'g/m²'] +[2025-07-04 00:16:11,652] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 1 치환됨 +[2025-07-04 00:16:11,676] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료 +[2025-07-04 00:16:11,690] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8141 사용 중 +[2025-07-04 00:16:11,691] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8141 사용 +[2025-07-04 00:16:22,804] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8141 유휴 +[2025-07-04 00:16:22,806] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료 +[2025-07-04 00:16:22,809] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 1 처리 중 오류: 'TextRenderingModule' object has no attribute 'default_font_path' +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\image_processor2.py", line 164, in process_single_image + text_rendered_image = self.text_rendering_module.render_text( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 101, in render_text + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 66, in calculate_optimal_font_size + width, height = self.estimate_text_size(text, size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 54, in estimate_text_size + font = self.get_font(font_size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 38, in get_font + font_path = font_path or self.default_font_path + ^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'TextRenderingModule' object has no attribute 'default_font_path' + +[2025-07-04 00:16:22,914] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 2 처리 시작: D:\py\AutoPercenty3\src\modules\img\2.jpg - OCR+인페인팅 모드 +[2025-07-04 00:16:22,916] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\2.jpg +[2025-07-04 00:16:22,955] [MainThread] [INFO] [ocr_module.py:detect_text:81] 🔍 OCR 감지 방식: polygon +[2025-07-04 00:16:24,277] [MainThread] [INFO] [ocr_module.py:detect_text:87] ocr_raw_results: [[[[[60.0, 60.0], [264.0, 63.0], [264.0, 92.0], [60.0, 89.0]], ('WA汉世刘家', 0.782518208026886)], [[[43.0, 129.0], [541.0, 129.0], [541.0, 182.0], [43.0, 182.0]], ('脱水比洗衣机更干', 0.994388222694397)], [[[38.0, 205.0], [394.0, 203.0], [394.0, 268.0], [39.0, 270.0]], ('真正免手洗', 0.98237144947052)]]] +[2025-07-04 00:16:24,278] [MainThread] [INFO] [ocr_module.py:detect_text:89] line: [[[[60.0, 60.0], [264.0, 63.0], [264.0, 92.0], [60.0, 89.0]], ('WA汉世刘家', 0.782518208026886)], [[[43.0, 129.0], [541.0, 129.0], [541.0, 182.0], [43.0, 182.0]], ('脱水比洗衣机更干', 0.994388222694397)], [[[38.0, 205.0], [394.0, 203.0], [394.0, 268.0], [39.0, 270.0]], ('真正免手洗', 0.98237144947052)]] +[2025-07-04 00:16:24,279] [MainThread] [INFO] [ocr_module.py:filter_chinese_text:143] 중국어 텍스트 3개 필터링 완료 +[2025-07-04 00:16:25,684] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['WA한세유가', '탈수는 세탁기보다 더 건조하다', '진정한 손세탁 면제'] +[2025-07-04 00:16:25,685] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 2 치환됨 +[2025-07-04 00:16:25,718] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료 +[2025-07-04 00:16:25,744] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8141 사용 중 +[2025-07-04 00:16:25,745] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8141 사용 +[2025-07-04 00:16:34,437] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8141 유휴 +[2025-07-04 00:16:34,439] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료 +[2025-07-04 00:16:34,442] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 2 처리 중 오류: 'TextRenderingModule' object has no attribute 'default_font_path' +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\image_processor2.py", line 164, in process_single_image + text_rendered_image = self.text_rendering_module.render_text( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 101, in render_text + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 66, in calculate_optimal_font_size + width, height = self.estimate_text_size(text, size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 54, in estimate_text_size + font = self.get_font(font_size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 38, in get_font + font_path = font_path or self.default_font_path + ^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'TextRenderingModule' object has no attribute 'default_font_path' + +[2025-07-04 00:16:34,444] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 4 처리 시작: D:\py\AutoPercenty3\src\modules\img\4.jpg - OCR+인페인팅 모드 +[2025-07-04 00:16:34,445] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\4.jpg +[2025-07-04 00:16:34,458] [MainThread] [INFO] [ocr_module.py:detect_text:81] 🔍 OCR 감지 방식: polygon +[2025-07-04 00:16:36,492] [MainThread] [INFO] [ocr_module.py:detect_text:87] ocr_raw_results: [[[[[431.0, 71.0], [467.0, 71.0], [467.0, 86.0], [431.0, 86.0]], ('85°C', 0.8005948662757874)], [[[516.0, 69.0], [558.0, 69.0], [558.0, 88.0], [516.0, 88.0]], ('185°F', 0.9477065801620483)], [[[430.0, 138.0], [469.0, 138.0], [469.0, 156.0], [430.0, 156.0]], ('80℃', 0.948918879032135)], [[[510.0, 138.0], [560.0, 136.0], [561.0, 154.0], [511.0, 156.0]], ('176F', 0.9774500727653503)], [[[431.0, 204.0], [487.0, 208.0], [486.0, 226.0], [430.0, 223.0]], ('70°℃', 0.8445274829864502)], [[[492.0, 208.0], [559.0, 204.0], [561.0, 222.0], [494.0, 226.0]], ('158F', 0.9599380493164062)], [[[430.0, 274.0], [469.0, 274.0], [469.0, 292.0], [430.0, 292.0]], ('60℃', 0.959047794342041)], [[[508.0, 275.0], [560.0, 275.0], [560.0, 292.0], [508.0, 292.0]], ('140°F', 0.9507576823234558)], [[[431.0, 344.0], [467.0, 344.0], [467.0, 359.0], [431.0, 359.0]], ('50°C', 0.8583885431289673)], [[[514.0, 342.0], [560.0, 342.0], [560.0, 360.0], [514.0, 360.0]], ('122°F', 0.8599249720573425)], [[[429.0, 412.0], [467.0, 409.0], [468.0, 428.0], [430.0, 431.0]], ('40C', 0.8281779885292053)], [[[515.0, 412.0], [559.0, 412.0], [559.0, 427.0], [515.0, 427.0]], ('104°F', 0.9546159505844116)], [[[24.0, 447.0], [227.0, 449.0], [226.0, 477.0], [24.0, 475.0]], ('精确的温度控制', 0.9948644042015076)], [[[25.0, 484.0], [262.0, 484.0], [262.0, 504.0], [25.0, 504.0]], ('温度范围从30℃至85℃', 0.9833672642707825)], [[[430.0, 480.0], [468.0, 480.0], [468.0, 497.0], [430.0, 497.0]], ('30°C', 0.7743486762046814)], [[[513.0, 480.0], [549.0, 480.0], [549.0, 497.0], [513.0, 497.0]], ('86°F', 0.9255594611167908)], [[[21.0, 514.0], [428.0, 512.0], [428.0, 536.0], [21.0, 538.0]], ('Temperature range from 86 F to 185°F', 0.9300384521484375)], [[[25.0, 542.0], [572.0, 543.0], [572.0, 570.0], [25.0, 569.0]], ('PRECISE TEMPERATURECONTROL', 0.9752708673477173)]]] +[2025-07-04 00:16:36,494] [MainThread] [INFO] [ocr_module.py:detect_text:89] line: [[[[431.0, 71.0], [467.0, 71.0], [467.0, 86.0], [431.0, 86.0]], ('85°C', 0.8005948662757874)], [[[516.0, 69.0], [558.0, 69.0], [558.0, 88.0], [516.0, 88.0]], ('185°F', 0.9477065801620483)], [[[430.0, 138.0], [469.0, 138.0], [469.0, 156.0], [430.0, 156.0]], ('80℃', 0.948918879032135)], [[[510.0, 138.0], [560.0, 136.0], [561.0, 154.0], [511.0, 156.0]], ('176F', 0.9774500727653503)], [[[431.0, 204.0], [487.0, 208.0], [486.0, 226.0], [430.0, 223.0]], ('70°℃', 0.8445274829864502)], [[[492.0, 208.0], [559.0, 204.0], [561.0, 222.0], [494.0, 226.0]], ('158F', 0.9599380493164062)], [[[430.0, 274.0], [469.0, 274.0], [469.0, 292.0], [430.0, 292.0]], ('60℃', 0.959047794342041)], [[[508.0, 275.0], [560.0, 275.0], [560.0, 292.0], [508.0, 292.0]], ('140°F', 0.9507576823234558)], [[[431.0, 344.0], [467.0, 344.0], [467.0, 359.0], [431.0, 359.0]], ('50°C', 0.8583885431289673)], [[[514.0, 342.0], [560.0, 342.0], [560.0, 360.0], [514.0, 360.0]], ('122°F', 0.8599249720573425)], [[[429.0, 412.0], [467.0, 409.0], [468.0, 428.0], [430.0, 431.0]], ('40C', 0.8281779885292053)], [[[515.0, 412.0], [559.0, 412.0], [559.0, 427.0], [515.0, 427.0]], ('104°F', 0.9546159505844116)], [[[24.0, 447.0], [227.0, 449.0], [226.0, 477.0], [24.0, 475.0]], ('精确的温度控制', 0.9948644042015076)], [[[25.0, 484.0], [262.0, 484.0], [262.0, 504.0], [25.0, 504.0]], ('温度范围从30℃至85℃', 0.9833672642707825)], [[[430.0, 480.0], [468.0, 480.0], [468.0, 497.0], [430.0, 497.0]], ('30°C', 0.7743486762046814)], [[[513.0, 480.0], [549.0, 480.0], [549.0, 497.0], [513.0, 497.0]], ('86°F', 0.9255594611167908)], [[[21.0, 514.0], [428.0, 512.0], [428.0, 536.0], [21.0, 538.0]], ('Temperature range from 86 F to 185°F', 0.9300384521484375)], [[[25.0, 542.0], [572.0, 543.0], [572.0, 570.0], [25.0, 569.0]], ('PRECISE TEMPERATURECONTROL', 0.9752708673477173)]] +[2025-07-04 00:16:36,495] [MainThread] [INFO] [ocr_module.py:filter_chinese_text:143] 중국어 텍스트 2개 필터링 완료 +[2025-07-04 00:16:39,915] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['85°C', '185°F', '80℃', '176F', '70°℃', '158F', '60℃', '140°F', '50°C', '122°F', '40C', '104°F', '정확한 온도 조절', '온도 범위는 30℃에서 85℃까지', '30°C', '86°F', '온도 범위는 86°F에서 185°F까지', '정확한 온도 조절'] +[2025-07-04 00:16:39,916] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 4 치환됨 +[2025-07-04 00:16:39,941] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료 +[2025-07-04 00:16:39,954] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8141 사용 중 +[2025-07-04 00:16:39,956] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8141 사용 +[2025-07-04 00:16:47,134] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8141 유휴 +[2025-07-04 00:16:47,135] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료 +[2025-07-04 00:16:47,137] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 4 처리 중 오류: 'TextRenderingModule' object has no attribute 'default_font_path' +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\image_processor2.py", line 164, in process_single_image + text_rendered_image = self.text_rendering_module.render_text( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 101, in render_text + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 66, in calculate_optimal_font_size + width, height = self.estimate_text_size(text, size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 54, in estimate_text_size + font = self.get_font(font_size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 38, in get_font + font_path = font_path or self.default_font_path + ^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'TextRenderingModule' object has no attribute 'default_font_path' + +[2025-07-04 00:16:47,138] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 7 처리 시작: D:\py\AutoPercenty3\src\modules\img\7.jpg - OCR+인페인팅 모드 +[2025-07-04 00:16:47,139] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\7.jpg +[2025-07-04 00:16:47,153] [MainThread] [INFO] [ocr_module.py:detect_text:81] 🔍 OCR 감지 방식: polygon +[2025-07-04 00:16:49,547] [MainThread] [INFO] [ocr_module.py:detect_text:87] ocr_raw_results: [[[[[74.0, 20.0], [157.0, 20.0], [157.0, 49.0], [74.0, 49.0]], ('科尔诺', 0.9947752952575684)], [[[243.0, 22.0], [291.0, 22.0], [291.0, 45.0], [243.0, 45.0]], ('MC', 0.6195889711380005)], [[[309.0, 22.0], [350.0, 24.0], [349.0, 43.0], [308.0, 41.0]], ('PA', 0.9959157705307007)], [[[372.0, 25.0], [423.0, 25.0], [423.0, 41.0], [372.0, 41.0]], ('CNEX', 0.9921520352363586)], [[[493.0, 17.0], [542.0, 17.0], [542.0, 48.0], [493.0, 48.0]], ('CE', 0.9579372406005859)], [[[561.0, 19.0], [611.0, 19.0], [611.0, 46.0], [561.0, 46.0]], ('SGS', 0.9938836693763733)], [[[75.0, 53.0], [158.0, 53.0], [158.0, 74.0], [75.0, 74.0]], ('KORNO', 0.9965262413024902)], [[[240.0, 57.0], [291.0, 57.0], [291.0, 71.0], [240.0, 71.0]], ('CMC认证', 0.9946392178535461)], [[[306.0, 57.0], [355.0, 57.0], [355.0, 71.0], [306.0, 71.0]], ('CPA认证', 0.9976503252983093)], [[[371.0, 57.0], [419.0, 57.0], [419.0, 71.0], [371.0, 71.0]], ('国家防爆', 0.9972963333129883)], [[[436.0, 57.0], [480.0, 57.0], [480.0, 71.0], [436.0, 71.0]], ('ISO认证', 0.996651291847229)], [[[500.0, 57.0], [539.0, 57.0], [539.0, 71.0], [500.0, 71.0]], ('CE认证', 0.9982097148895264)], [[[562.0, 57.0], [609.0, 57.0], [609.0, 71.0], [562.0, 71.0]], ('SGS认证', 0.9988522529602051)], [[[45.0, 101.0], [283.0, 101.0], [283.0, 146.0], [45.0, 146.0]], ('GT-1000', 0.9948582053184509)], [[[45.0, 166.0], [407.0, 166.0], [407.0, 211.0], [45.0, 211.0]], ('激光粉尘检测仪', 0.9980788230895996)], [[[29.0, 239.0], [216.0, 237.0], [216.0, 264.0], [29.0, 266.0]], ('精度≤±5%F.S', 0.9504798054695129)], [[[28.0, 297.0], [162.0, 297.0], [162.0, 324.0], [28.0, 324.0]], ('防护等级:', 0.9984332323074341)], [[[148.0, 295.0], [236.0, 295.0], [236.0, 323.0], [148.0, 323.0]], (':IP65', 0.9789118766784668)], [[[23.0, 356.0], [420.0, 356.0], [420.0, 380.0], [23.0, 380.0]], ('过压保护/声光报警/存储打印', 0.9622747302055359)], [[[21.0, 410.0], [425.0, 413.0], [425.0, 440.0], [21.0, 437.0]], ('PM0.3/0.5/1.0/2.5/5.0/10um', 0.9829097986221313)], [[[29.0, 469.0], [371.0, 469.0], [371.0, 492.0], [29.0, 492.0]], ('可同时监测多种粒径尘埃粒子数', 0.9967579245567322)], [[[29.0, 505.0], [271.0, 505.0], [271.0, 526.0], [29.0, 526.0]], ('适合十万级以上洁净室', 0.9917163848876953)], [[[15.0, 532.0], [119.0, 532.0], [119.0, 589.0], [15.0, 589.0]], ('全国', 0.9994618892669678)], [[[194.0, 544.0], [391.0, 544.0], [391.0, 571.0], [194.0, 571.0]], ('7天无理由退货', 0.9978777766227722)], [[[428.0, 543.0], [542.0, 543.0], [542.0, 571.0], [428.0, 571.0]], ('赠运险费', 0.998300313949585)], [[[15.0, 585.0], [122.0, 587.0], [121.0, 639.0], [14.0, 637.0]], ('包邮', 0.9907819032669067)], [[[138.0, 585.0], [625.0, 583.0], [625.0, 617.0], [138.0, 619.0]], ('原厂正品/可开发票/质保一年', 0.9910593032836914)]]] +[2025-07-04 00:16:49,549] [MainThread] [INFO] [ocr_module.py:detect_text:89] line: [[[[74.0, 20.0], [157.0, 20.0], [157.0, 49.0], [74.0, 49.0]], ('科尔诺', 0.9947752952575684)], [[[243.0, 22.0], [291.0, 22.0], [291.0, 45.0], [243.0, 45.0]], ('MC', 0.6195889711380005)], [[[309.0, 22.0], [350.0, 24.0], [349.0, 43.0], [308.0, 41.0]], ('PA', 0.9959157705307007)], [[[372.0, 25.0], [423.0, 25.0], [423.0, 41.0], [372.0, 41.0]], ('CNEX', 0.9921520352363586)], [[[493.0, 17.0], [542.0, 17.0], [542.0, 48.0], [493.0, 48.0]], ('CE', 0.9579372406005859)], [[[561.0, 19.0], [611.0, 19.0], [611.0, 46.0], [561.0, 46.0]], ('SGS', 0.9938836693763733)], [[[75.0, 53.0], [158.0, 53.0], [158.0, 74.0], [75.0, 74.0]], ('KORNO', 0.9965262413024902)], [[[240.0, 57.0], [291.0, 57.0], [291.0, 71.0], [240.0, 71.0]], ('CMC认证', 0.9946392178535461)], [[[306.0, 57.0], [355.0, 57.0], [355.0, 71.0], [306.0, 71.0]], ('CPA认证', 0.9976503252983093)], [[[371.0, 57.0], [419.0, 57.0], [419.0, 71.0], [371.0, 71.0]], ('国家防爆', 0.9972963333129883)], [[[436.0, 57.0], [480.0, 57.0], [480.0, 71.0], [436.0, 71.0]], ('ISO认证', 0.996651291847229)], [[[500.0, 57.0], [539.0, 57.0], [539.0, 71.0], [500.0, 71.0]], ('CE认证', 0.9982097148895264)], [[[562.0, 57.0], [609.0, 57.0], [609.0, 71.0], [562.0, 71.0]], ('SGS认证', 0.9988522529602051)], [[[45.0, 101.0], [283.0, 101.0], [283.0, 146.0], [45.0, 146.0]], ('GT-1000', 0.9948582053184509)], [[[45.0, 166.0], [407.0, 166.0], [407.0, 211.0], [45.0, 211.0]], ('激光粉尘检测仪', 0.9980788230895996)], [[[29.0, 239.0], [216.0, 237.0], [216.0, 264.0], [29.0, 266.0]], ('精度≤±5%F.S', 0.9504798054695129)], [[[28.0, 297.0], [162.0, 297.0], [162.0, 324.0], [28.0, 324.0]], ('防护等级:', 0.9984332323074341)], [[[148.0, 295.0], [236.0, 295.0], [236.0, 323.0], [148.0, 323.0]], (':IP65', 0.9789118766784668)], [[[23.0, 356.0], [420.0, 356.0], [420.0, 380.0], [23.0, 380.0]], ('过压保护/声光报警/存储打印', 0.9622747302055359)], [[[21.0, 410.0], [425.0, 413.0], [425.0, 440.0], [21.0, 437.0]], ('PM0.3/0.5/1.0/2.5/5.0/10um', 0.9829097986221313)], [[[29.0, 469.0], [371.0, 469.0], [371.0, 492.0], [29.0, 492.0]], ('可同时监测多种粒径尘埃粒子数', 0.9967579245567322)], [[[29.0, 505.0], [271.0, 505.0], [271.0, 526.0], [29.0, 526.0]], ('适合十万级以上洁净室', 0.9917163848876953)], [[[15.0, 532.0], [119.0, 532.0], [119.0, 589.0], [15.0, 589.0]], ('全国', 0.9994618892669678)], [[[194.0, 544.0], [391.0, 544.0], [391.0, 571.0], [194.0, 571.0]], ('7天无理由退货', 0.9978777766227722)], [[[428.0, 543.0], [542.0, 543.0], [542.0, 571.0], [428.0, 571.0]], ('赠运险费', 0.998300313949585)], [[[15.0, 585.0], [122.0, 587.0], [121.0, 639.0], [14.0, 637.0]], ('包邮', 0.9907819032669067)], [[[138.0, 585.0], [625.0, 583.0], [625.0, 617.0], [138.0, 619.0]], ('原厂正品/可开发票/质保一年', 0.9910593032836914)]] +[2025-07-04 00:16:49,551] [MainThread] [INFO] [ocr_module.py:filter_chinese_text:143] 중국어 텍스트 18개 필터링 완료 +[2025-07-04 00:16:54,046] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['코르노', 'MC', 'PA', 'CNEX', 'CE', 'SGS', 'KORNO', 'CMC 인증', 'CPA 인증', '국가 방폭', 'ISO 인증', 'CE 인증', 'SGS 인증', 'GT-1000', '레이저 분진 측정기', '정밀도 ≤ ±5%F.S', '방호 등급:', ':IP65', '과전압 보호/음성 및 광경고/저장 및 인쇄', 'PM0.3/0.5/1.0/2.5/5.0/10um', '다양한 입자 크기의 먼지 입자를 동시에 모니터링 가능', '10만 급 이상의 청정실에 적합', '전국', '7일 무조건 반품', '운송 보험료 무료', '무료 배송', '정품/세금계산서 발행 가능/1년 보증'] +[2025-07-04 00:16:54,047] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 7 치환됨 +[2025-07-04 00:16:54,068] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료 +[2025-07-04 00:16:54,081] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8141 사용 중 +[2025-07-04 00:16:54,083] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8141 사용 +[2025-07-04 00:17:01,691] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8141 유휴 +[2025-07-04 00:17:01,693] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료 +[2025-07-04 00:17:01,695] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 7 처리 중 오류: 'TextRenderingModule' object has no attribute 'default_font_path' +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\image_processor2.py", line 164, in process_single_image + text_rendered_image = self.text_rendering_module.render_text( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 101, in render_text + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 66, in calculate_optimal_font_size + width, height = self.estimate_text_size(text, size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 54, in estimate_text_size + font = self.get_font(font_size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 38, in get_font + font_path = font_path or self.default_font_path + ^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'TextRenderingModule' object has no attribute 'default_font_path' + +[2025-07-04 00:17:01,698] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 6 처리 시작: D:\py\AutoPercenty3\src\modules\img\6.jpg - OCR+인페인팅 모드 +[2025-07-04 00:17:01,699] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\6.jpg +[2025-07-04 00:17:01,740] [MainThread] [INFO] [ocr_module.py:detect_text:81] 🔍 OCR 감지 방식: polygon +[2025-07-04 00:17:03,115] [MainThread] [INFO] [ocr_module.py:detect_text:87] ocr_raw_results: [[[[[344.0, 108.0], [855.0, 108.0], [855.0, 182.0], [344.0, 182.0]], ('现代极简风格', 0.9951332211494446)], [[[235.0, 219.0], [964.0, 219.0], [964.0, 279.0], [235.0, 279.0]], ('更易搭配各种使用场景', 0.9972091913223267)], [[[136.0, 447.0], [717.0, 447.0], [717.0, 534.0], [136.0, 534.0]], ('半圆两端设计', 0.9981253743171692)], [[[134.0, 571.0], [715.0, 571.0], [715.0, 658.0], [134.0, 658.0]], ('承载各种欢乐', 0.9950291514396667)]]] +[2025-07-04 00:17:03,116] [MainThread] [INFO] [ocr_module.py:detect_text:89] line: [[[[344.0, 108.0], [855.0, 108.0], [855.0, 182.0], [344.0, 182.0]], ('现代极简风格', 0.9951332211494446)], [[[235.0, 219.0], [964.0, 219.0], [964.0, 279.0], [235.0, 279.0]], ('更易搭配各种使用场景', 0.9972091913223267)], [[[136.0, 447.0], [717.0, 447.0], [717.0, 534.0], [136.0, 534.0]], ('半圆两端设计', 0.9981253743171692)], [[[134.0, 571.0], [715.0, 571.0], [715.0, 658.0], [134.0, 658.0]], ('承载各种欢乐', 0.9950291514396667)]] +[2025-07-04 00:17:03,118] [MainThread] [INFO] [ocr_module.py:filter_chinese_text:143] 중국어 텍스트 4개 필터링 완료 +[2025-07-04 00:17:04,388] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['현대 미니멀 스타일', '다양한 사용 장면에 더 쉽게 어울림', '반원 양끝 디자인', '다양한 즐거움을 담다'] +[2025-07-04 00:17:04,389] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 6 치환됨 +[2025-07-04 00:17:04,457] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료 +[2025-07-04 00:17:04,514] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8141 사용 중 +[2025-07-04 00:17:04,515] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8141 사용 +[2025-07-04 00:17:23,630] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8141 유휴 +[2025-07-04 00:17:23,632] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료 +[2025-07-04 00:17:23,636] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 6 처리 중 오류: 'TextRenderingModule' object has no attribute 'default_font_path' +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\image_processor2.py", line 164, in process_single_image + text_rendered_image = self.text_rendering_module.render_text( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 101, in render_text + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 66, in calculate_optimal_font_size + width, height = self.estimate_text_size(text, size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 54, in estimate_text_size + font = self.get_font(font_size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 38, in get_font + font_path = font_path or self.default_font_path + ^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'TextRenderingModule' object has no attribute 'default_font_path' + +[2025-07-04 00:17:23,639] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 3 처리 시작: D:\py\AutoPercenty3\src\modules\img\3.jpg - OCR+인페인팅 모드 +[2025-07-04 00:17:23,640] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\3.jpg +[2025-07-04 00:17:23,662] [MainThread] [INFO] [ocr_module.py:detect_text:81] 🔍 OCR 감지 방식: polygon +[2025-07-04 00:17:25,128] [MainThread] [INFO] [ocr_module.py:detect_text:87] ocr_raw_results: [[[[[196.0, 55.0], [405.0, 55.0], [405.0, 72.0], [196.0, 72.0]], ('MICROCULTIVATOR-', 0.9893437027931213)], [[[183.0, 102.0], [423.0, 102.0], [423.0, 160.0], [183.0, 160.0]], ('轻轻一拉', 0.9972113370895386)], [[[78.0, 179.0], [525.0, 179.0], [525.0, 227.0], [78.0, 227.0]], ('3秒即可快速启动', 0.996617317199707)], [[[98.0, 266.0], [506.0, 266.0], [506.0, 286.0], [98.0, 286.0]], ('加快机器供油,燃烧,传动流程3秒快速启动', 0.9955319166183472)], [[[178.0, 290.0], [430.0, 290.0], [430.0, 310.0], [178.0, 310.0]], ('让您不用浪费时间在启动上', 0.9967721104621887)]]] +[2025-07-04 00:17:25,129] [MainThread] [INFO] [ocr_module.py:detect_text:89] line: [[[[196.0, 55.0], [405.0, 55.0], [405.0, 72.0], [196.0, 72.0]], ('MICROCULTIVATOR-', 0.9893437027931213)], [[[183.0, 102.0], [423.0, 102.0], [423.0, 160.0], [183.0, 160.0]], ('轻轻一拉', 0.9972113370895386)], [[[78.0, 179.0], [525.0, 179.0], [525.0, 227.0], [78.0, 227.0]], ('3秒即可快速启动', 0.996617317199707)], [[[98.0, 266.0], [506.0, 266.0], [506.0, 286.0], [98.0, 286.0]], ('加快机器供油,燃烧,传动流程3秒快速启动', 0.9955319166183472)], [[[178.0, 290.0], [430.0, 290.0], [430.0, 310.0], [178.0, 310.0]], ('让您不用浪费时间在启动上', 0.9967721104621887)]] +[2025-07-04 00:17:25,130] [MainThread] [INFO] [ocr_module.py:filter_chinese_text:143] 중국어 텍스트 4개 필터링 완료 +[2025-07-04 00:17:27,264] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['마이크로 경작기-', '가볍게 당기면', '3초 만에 빠르게 시작됩니다.', '기계의 연료 공급, 연소, 전송 프로세스를 3초 만에 빠르게 시작합니다.', '시작하는 데 시간을 낭비하지 않도록 도와드립니다.'] +[2025-07-04 00:17:27,267] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 3 치환됨 +[2025-07-04 00:17:27,287] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료 +[2025-07-04 00:17:27,306] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8141 사용 중 +[2025-07-04 00:17:27,307] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8141 사용 +[2025-07-04 00:17:37,642] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8141 유휴 +[2025-07-04 00:17:37,645] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료 +[2025-07-04 00:17:37,648] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 3 처리 중 오류: 'TextRenderingModule' object has no attribute 'default_font_path' +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\image_processor2.py", line 164, in process_single_image + text_rendered_image = self.text_rendering_module.render_text( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 101, in render_text + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 66, in calculate_optimal_font_size + width, height = self.estimate_text_size(text, size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 54, in estimate_text_size + font = self.get_font(font_size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 38, in get_font + font_path = font_path or self.default_font_path + ^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'TextRenderingModule' object has no attribute 'default_font_path' + +[2025-07-04 00:17:37,650] [MainThread] [INFO] [image_processor2.py:process_single_image:125] 이미지 5 처리 시작: D:\py\AutoPercenty3\src\modules\img\5.jpg - OCR+인페인팅 모드 +[2025-07-04 00:17:37,651] [MainThread] [INFO] [image_processor2.py:download_image:200] 로컬 파일 경로 감지, 다운로드 생략: D:\py\AutoPercenty3\src\modules\img\5.jpg +[2025-07-04 00:17:37,725] [MainThread] [INFO] [ocr_module.py:detect_text:81] 🔍 OCR 감지 방식: polygon +[2025-07-04 00:17:39,340] [MainThread] [INFO] [ocr_module.py:detect_text:87] ocr_raw_results: [[[[[339.0, 103.0], [855.0, 103.0], [855.0, 182.0], [339.0, 182.0]], ('现代极简风格', 0.996478259563446)], [[[235.0, 219.0], [963.0, 219.0], [963.0, 279.0], [235.0, 279.0]], ('更易搭配各种使用场景', 0.9974321126937866)], [[[478.0, 727.0], [649.0, 727.0], [649.0, 762.0], [478.0, 762.0]], ('★WELCOME', 0.8576037883758545)], [[[407.0, 760.0], [724.0, 760.0], [724.0, 855.0], [407.0, 855.0]], ('欢迎光临', 0.9999475479125977)], [[[473.0, 856.0], [624.0, 863.0], [623.0, 898.0], [471.0, 891.0]], ('限时促销礼惠全城', 0.9319560527801514)], [[[446.0, 947.0], [640.0, 962.0], [638.0, 995.0], [443.0, 980.0]], ('满499减200/满999减500', 0.9274235963821411)], [[[481.0, 980.0], [595.0, 991.0], [592.0, 1016.0], [479.0, 1005.0]], ('动的间167', 0.7949853539466858)]]] +[2025-07-04 00:17:39,341] [MainThread] [INFO] [ocr_module.py:detect_text:89] line: [[[[339.0, 103.0], [855.0, 103.0], [855.0, 182.0], [339.0, 182.0]], ('现代极简风格', 0.996478259563446)], [[[235.0, 219.0], [963.0, 219.0], [963.0, 279.0], [235.0, 279.0]], ('更易搭配各种使用场景', 0.9974321126937866)], [[[478.0, 727.0], [649.0, 727.0], [649.0, 762.0], [478.0, 762.0]], ('★WELCOME', 0.8576037883758545)], [[[407.0, 760.0], [724.0, 760.0], [724.0, 855.0], [407.0, 855.0]], ('欢迎光临', 0.9999475479125977)], [[[473.0, 856.0], [624.0, 863.0], [623.0, 898.0], [471.0, 891.0]], ('限时促销礼惠全城', 0.9319560527801514)], [[[446.0, 947.0], [640.0, 962.0], [638.0, 995.0], [443.0, 980.0]], ('满499减200/满999减500', 0.9274235963821411)], [[[481.0, 980.0], [595.0, 991.0], [592.0, 1016.0], [479.0, 1005.0]], ('动的间167', 0.7949853539466858)]] +[2025-07-04 00:17:39,343] [MainThread] [INFO] [ocr_module.py:filter_chinese_text:143] 중국어 텍스트 6개 필터링 완료 +[2025-07-04 00:17:41,499] [MainThread] [INFO] [image_processor2.py:process_translated_texts:298] 최종 치환 결과: ['현대 미니멀리즘 스타일', '다양한 사용 장면에 더 쉽게 어울립니다', '★환영합니다', '환영합니다', '한정 시간 프로모션, 전 도시 할인', '499원 이상 구매 시 200원 할인 / 999원 이상 구매 시 500원 할인', '동의 간 167'] +[2025-07-04 00:17:41,500] [MainThread] [INFO] [image_processor2.py:process_single_image:150] 이미지 5 치환됨 +[2025-07-04 00:17:41,563] [MainThread] [INFO] [image_processor2.py:process_single_image:156] 마스크 생성 완료 +[2025-07-04 00:17:41,605] [MainThread] [INFO] [iop_Manager.py:get_idle_instance:113] IOPaint 인스턴스 8141 사용 중 +[2025-07-04 00:17:41,606] [MainThread] [INFO] [iop_Manager.py:inpaint:161] IOPaint 인스턴스 8141 사용 +[2025-07-04 00:17:57,890] [MainThread] [INFO] [iop_Manager.py:mark_instance_idle:122] IOPaint 인스턴스 8141 유휴 +[2025-07-04 00:17:57,893] [MainThread] [INFO] [image_processor2.py:process_single_image:161] 인페인팅 완료 +[2025-07-04 00:17:57,897] [MainThread] [ERROR] [image_processor2.py:process_single_image:174] 이미지 5 처리 중 오류: 'TextRenderingModule' object has no attribute 'default_font_path' +Traceback (most recent call last): + File "D:\py\AutoPercenty3\src\modules\image_processor2.py", line 164, in process_single_image + text_rendered_image = self.text_rendering_module.render_text( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 101, in render_text + optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 66, in calculate_optimal_font_size + width, height = self.estimate_text_size(text, size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 54, in estimate_text_size + font = self.get_font(font_size, font_path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\py\AutoPercenty3\src\modules\text_rendering_module.py", line 38, in get_font + font_path = font_path or self.default_font_path + ^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'TextRenderingModule' object has no attribute 'default_font_path' + diff --git a/modules/clipboardImageManager copy.py b/modules/clipboardImageManager copy.py new file mode 100644 index 0000000..fefa400 --- /dev/null +++ b/modules/clipboardImageManager copy.py @@ -0,0 +1,764 @@ +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] + + # 이미지 크기 + width, height = image.size + + # 워터마크 레이어 생성 + watermark_layer = Image.new("RGBA", (width, height)) # RGBA 이미지 생성 + + # 지그재그 간격 설정 + zigzag_step = int(text_height * 2) # Y축의 지그재그 간격 + + + # 이미지 전체에 반복적으로 워터마크 텍스트 그리기 (지그재그 형태) + for y in range(0, height, zigzag_step): + for x in range(0, width, int(text_width * 3)): # 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/gpt_client.py b/modules/gpt_client.py new file mode 100644 index 0000000..9228961 --- /dev/null +++ b/modules/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/image_processor2.py b/modules/image_processor2.py new file mode 100644 index 0000000..c00fc14 --- /dev/null +++ b/modules/image_processor2.py @@ -0,0 +1,299 @@ +import os +import asyncio +import aiofiles +import logging +from urllib.parse import urlparse +import shutil +import sys +from concurrent.futures import ThreadPoolExecutor +import re +import cv2 +import base64 +import requests +import numpy as np +from modules.ocr_module import OCRModule +from modules.mask_module import MaskModule +from modules.text_rendering_module import TextRenderingModule +from modules.postImageManager import PostImageManager + +class ImageProcessor: + """이미지 다운로드, OCR, 번역 처리를 담당하는 클래스""" + + def __init__(self, logger, gpt_client, base_dir, font_path): + self.logger = logger + self.base_dir = base_dir + self.gpt_client = gpt_client + + # OCR 관련 + self.inpaint_sv_port = 8080 + + self.font_path = font_path + self.TEMP_IMAGE_DIR = os.path.join(self.base_dir, "temp_images") + os.makedirs(self.TEMP_IMAGE_DIR, exist_ok=True) + + 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, font_path=self.font_path) + + 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, toggle_states, unwanted_texts, local_image_path, index, file_prefix=""): + """ + 단일 이미지를 처리합니다 (다운로드 -> OCR -> 인페인팅) + + Args: + toggle_states: 토글 상태 딕셔너리 + local_image_path (str): 처리할 이미지 경로 + index (int): 이미지 인덱스 + unwanted_texts: 치환할 텍스트 딕셔너리 + file_prefix (str): 파일명에 추가할 접두사 (예: "detail", "option") + + Returns: + dict: 처리 결과를 포함한 딕셔너리 + - status: 'inpainted', 'original', 'exclude', 'error' 중 하나 + - path: 처리된 이미지 파일 경로 또는 원본 이미지 파일 경로 + - error: 오류 메시지 (status가 'error'인 경우에만 포함) + """ + ocr_enabled = toggle_states.get('ocr', False) + unwanted_texts = unwanted_texts + + try: + 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 local_image_path + + # 4. 텍스트 번역 (GPT) + translated_texts = self.gpt_translate_texts(ocr_results, self.gpt_client) + + if ocr_enabled: + filtered_translated_texts = self.process_translated_texts(translated_texts, unwanted_texts, local_image_path, index) + if not filtered_translated_texts: + self.logger.log(f"이미지 {index+1} 제외됨", level=logging.INFO) + return None + else: + self.logger.log(f"이미지 {index+1} 치환됨", 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.call_inpaint_api(local_image_path, masks) + self.logger.log(f"인페인팅 완료", level=logging.INFO) + + # 텍스트 렌더링 + text_rendered_image = self.text_rendering_module.render_text( + inpainted_image, ocr_results, filtered_translated_texts, font_path=self.font_path) + 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, toggle_states) + self.logger.log(f"이미지 {index+1} 번역 완료: {translated_img_path}", level=logging.INFO) + return 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, 'error': str(e)} + + async def postProcess_and_save_image(self, local_image_path, text_rendered_image, index, file_prefix, toggle_states): + """로컬 서버 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=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 + + 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 call_inpaint_api(self, image, mask): + """ + 인페인팅 API를 호출하여 이미지를 인페인팅합니다. + """ + try: + # 이미지 처리 + if isinstance(image, str): + image_np = cv2.imread(image) + if image_np is None: + self.logger.log(f"이미지 로딩 실패: {image}", level=logging.ERROR) + return None + else: + image_np = image + + # 마스크 처리 + if isinstance(mask, str): + mask_np = cv2.imread(mask, cv2.IMREAD_GRAYSCALE) + if mask_np is None: + self.logger.log(f"마스크 로딩 실패: {mask}", level=logging.ERROR) + return None + else: + mask_np = mask + + api_url = f"http://localhost:{self.inpaint_sv_port}/api/v1/inpaint" + _, img_encoded = cv2.imencode('.png', image_np) + _, mask_encoded = cv2.imencode('.png', mask_np) + 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: + self.logger.log(f"IOPaint 서버 에러: {response.text}", level=logging.ERROR) + return None + nparr = np.frombuffer(response.content, np.uint8) + result = cv2.imdecode(nparr, cv2.IMREAD_COLOR) + return result + except Exception as e: + self.logger.log(f"인페인팅 API 호출 중 오류: {e}", level=logging.ERROR, exc_info=True) + return None + + + 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, image_urls, 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( + url, i, 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/image_translate_server.py b/modules/image_translate_server.py new file mode 100644 index 0000000..d21f488 --- /dev/null +++ b/modules/image_translate_server.py @@ -0,0 +1,104 @@ +import random +import socket +import uvicorn +from fastapi import FastAPI, Query, Body +from pydantic import BaseModel +from typing import List, Optional +import asyncio +from concurrent.futures import ThreadPoolExecutor +from modules.image_processor2 import ImageProcessor + +# 포트 범위 설정 +PORT_RANGE = (7000, 7000) + +# 사용 가능한 포트 찾기 +def find_free_port(): + for _ in range(20): + port = random.randint(*PORT_RANGE) + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + try: + s.bind(("127.0.0.1", port)) + return port + except OSError: + continue + raise RuntimeError("사용 가능한 포트를 찾을 수 없습니다.") + +# 요청 모델 정의 +class ImageRequest(BaseModel): + local_image_path: str + file_prefix: Optional[str] = "" + use_inpainting: Optional[bool] = False + toggle_states: Optional[dict] = None + unwanted_texts: Optional[dict] = None + watermark_text: Optional[str] = None + watermark_opacity: Optional[float] = None + +class ImagesRequest(BaseModel): + local_image_paths: List[str] + file_prefix: Optional[str] = "" + use_inpainting: Optional[bool] = False + toggle_states: Optional[dict] = None + unwanted_texts: Optional[dict] = None + watermark_text: Optional[str] = None + watermark_opacity: Optional[float] = None + +# FastAPI 앱 생성 +def create_app(image_processor: ImageProcessor, max_workers: int = 2): + app = FastAPI() + executor = ThreadPoolExecutor(max_workers=max_workers) + + @app.post("/translate_image") + async def translate_image(req: ImageRequest): + # 워터마크 관련 옵션을 toggle_states에 병합 + toggle_states = req.toggle_states.copy() if req.toggle_states else {} + if req.watermark_text is not None: + toggle_states["watermark_text"] = req.watermark_text + if req.watermark_opacity is not None: + toggle_states["watermark_opacity"] = req.watermark_opacity + # 단일 이미지 번역 + result = await image_processor.process_single_image( + toggle_states, req.unwanted_texts or {}, req.local_image_path, 0, req.file_prefix + ) + # 경로만 반환 + if isinstance(result, dict): + return {"result": result.get("path", None)} + return {"result": result} + + @app.post("/translate_images") + async def translate_images(req: ImagesRequest): + # 워터마크 관련 옵션을 toggle_states에 병합 + toggle_states = req.toggle_states.copy() if req.toggle_states else {} + if req.watermark_text is not None: + toggle_states["watermark_text"] = req.watermark_text + if req.watermark_opacity is not None: + toggle_states["watermark_opacity"] = req.watermark_opacity + # 여러 이미지 병렬 번역 + loop = asyncio.get_event_loop() + tasks = [] + sem = asyncio.Semaphore(max_workers) + async def sem_task(idx, path): + async with sem: + return await image_processor.process_single_image( + toggle_states, req.unwanted_texts or {}, path, idx, req.file_prefix + ) + for idx, path in enumerate(req.local_image_paths): + tasks.append(sem_task(idx, path)) + results = await asyncio.gather(*tasks) + # 경로만 리스트로 반환 + def extract_path(res): + if isinstance(res, dict): + return res.get("path", None) + return res + return {"results": [extract_path(r) for r in results]} + + return app + +# 서버 실행 함수 +def run_server(image_processor, max_workers=2): + port = find_free_port() + app = create_app(image_processor, max_workers) + uvicorn.run(app, host="127.0.0.1", port=port, workers=1) + # FastAPI의 workers는 프로세스 수이므로, 내부 병렬은 ThreadPoolExecutor로 제어 + # 실제 워커 수는 process_single_image 병렬 호출로 제한 + # 서버 실행 후 포트 정보 반환 가능 + return port \ No newline at end of file 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..7a7339f 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/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/iop_Manager.py b/modules/iop_Manager.py new file mode 100644 index 0000000..98421c3 --- /dev/null +++ b/modules/iop_Manager.py @@ -0,0 +1,199 @@ +import numpy as np +import requests +import cv2 +import base64 +import subprocess +import random +import time +import threading +import os +import logging + +class IOPaintManager: + """IOPaint 서버 인스턴스 및 인페인팅 요청을 통합 관리하는 매니저""" + class ServerInstance: + def __init__(self, port, process): + self.port = port + self.process = process + self.busy = False + self.last_used = time.time() + def mark_busy(self): + self.busy = True + self.last_used = time.time() + def mark_idle(self): + self.busy = False + self.last_used = time.time() + def is_alive(self): + return self.process.poll() is None + + def __init__(self, logger, num_instances=1, port_range=(8099, 8199), base_dir=None, wait_ready=30, model_dir=None): + self.logger = logger + self.instances = [] + self.port_range = port_range + self.lock = threading.Lock() + self.base_dir = base_dir or os.getcwd() + self.model_dir = model_dir or os.path.join(self.base_dir, 'iop', 'models') + self.exe_path = os.path.join(self.base_dir, 'iop', 'iop.exe') + self._start_instances(num_instances, wait_ready) + + def _get_random_port(self): + used_ports = {inst.port for inst in self.instances} + candidates = [p for p in range(self.port_range[0], self.port_range[1]+1) if p not in used_ports] + if not candidates: + self.logger.log("사용 가능한 포트가 없습니다.", level=logging.ERROR) + raise RuntimeError("사용 가능한 포트가 없습니다.") + return random.choice(candidates) + + def wait_for_server_ready(self, port, timeout=30): + url = f"http://localhost:{port}/api/v1/server-config" + start = time.time() + last_error = None + self.logger.log(f"[{port}] 서버 준비 체크 시작 (최대 {timeout}초 대기)", level=logging.INFO) + tries = 0 + while time.time() - start < timeout: + tries += 1 + try: + r = requests.get(url, timeout=2) + self.logger.log(f"응답 : {r}", level=logging.INFO) + if r.status_code == 200: + elapsed = time.time() - start + self.logger.log(f"[{port}] 서버 준비 완료! (시도 {tries}회, {elapsed:.1f}초 소요)", level=logging.INFO) + return True + else: + self.logger.log(f"[{port}] 응답 코드: {r.status_code}", level=logging.INFO) + except Exception as e: + last_error = str(e) + self.logger.log(f"[{port}] 준비 체크 실패 (시도 {tries}회): {last_error}", level=logging.ERROR, exc_info=True) + time.sleep(0.5) + self.logger.log(f"[{port}] 서버 준비 실패 (총 {tries}회 시도, 마지막 에러: {last_error})", level=logging.ERROR, exc_info=True) + return False + + def _start_instances(self, num, wait_ready): + self.logger.log(f"IOPaint 인스턴스 {num} 개 시작", level=logging.INFO) + for _ in range(num): + port = self._get_random_port() + cmd = [self.exe_path, 'start', '--model=lama', '--device=cpu', '--port', str(port), '--model-dir', self.model_dir] + self.logger.log(f"[{port}] 인스턴스 실행 명령: {' '.join(cmd)}", level=logging.INFO) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + instance = self.ServerInstance(port, proc) + self.instances.append(instance) + start_wait = 8 + time.sleep(start_wait) + self.logger.log(f"[{port}] 인스턴스 실행 명시대기: {start_wait}초", level=logging.INFO) + if self.wait_for_server_ready(port, timeout=wait_ready): + self.logger.log(f"IOPaint 인스턴스 {instance.port} 준비됨", level=logging.INFO) + else: + self.logger.log(f"IOPaint 인스턴스 {instance.port} 시작 실패", level=logging.ERROR) + # 에러 메시지 출력 + try: + out, err = proc.communicate(timeout=3) + self.logger.log(f"[{port}] 표준출력:\n{out.decode(errors='ignore')}", level=logging.INFO) + self.logger.log(f"[{port}] 표준에러:\n{err.decode(errors='ignore')}", level=logging.INFO) + except Exception as e: + self.logger.log(f"[{port}] 에러 메시지 읽기 실패: {e}", level=logging.ERROR) + + def get_instance_info(self): + """모든 인스턴스의 정보를 반환""" + info = [] + for inst in self.instances: + info.append({ + "port": inst.port, + "busy": inst.busy, + "alive": inst.is_alive(), + "last_used": inst.last_used + }) + return info + + def get_idle_instance(self): + """놀고 있는(사용 가능한) 인스턴스 반환 (없으면 None)""" + with self.lock: + for inst in self.instances: + if not inst.busy and inst.is_alive(): + inst.mark_busy() + self.logger.log(f"IOPaint 인스턴스 {inst.port} 사용 중", level=logging.INFO) + return inst + return None + + def mark_instance_idle(self, port): + """작업이 끝난 인스턴스를 idle로 표시""" + for inst in self.instances: + if inst.port == port: + inst.mark_idle() + self.logger.log(f"IOPaint 인스턴스 {inst.port} 유휴", level=logging.INFO) + break + + def shutdown_all(self): + """모든 서버 인스턴스 종료""" + for inst in self.instances: + if inst.is_alive(): + inst.process.terminate() + self.logger.log(f"IOPaint 인스턴스 {inst.port} 종료", level=logging.INFO) + self.instances = [] + self.logger.log("모든 IOPaint 인스턴스 종료", level=logging.INFO) + + def inpaint(self, image, mask, instance=None) -> np.ndarray: + """image와 mask를 경로나 np.ndarray 모두 지원""" + # 이미지 처리 + if isinstance(image, str): + image_np = cv2.imread(image) + if image_np is None: + self.logger.log(f"이미지 로딩 실패: {image}", level=logging.ERROR) + return None + else: + image_np = image + + # 마스크 처리 + if isinstance(mask, str): + mask_np = cv2.imread(mask, cv2.IMREAD_GRAYSCALE) + if mask_np is None: + self.logger.log(f"마스크 로딩 실패: {mask}", level=logging.ERROR) + return None + else: + mask_np = mask + + + if instance is None: + instance = self.get_idle_instance() + if instance is None: + self.logger.log("사용 가능한 IOPaint 인스턴스가 없습니다.", level=logging.ERROR) + return None + api_url = f"http://localhost:{instance.port}/api/v1/inpaint" + self.logger.log(f"IOPaint 인스턴스 {instance.port} 사용", level=logging.INFO) + try: + _, img_encoded = cv2.imencode('.png', image_np) + _, mask_encoded = cv2.imencode('.png', mask_np) + 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: + self.logger.log(f"IOPaint 서버 에러: {response.text}", level=logging.ERROR) + return None + nparr = np.frombuffer(response.content, np.uint8) + result = cv2.imdecode(nparr, cv2.IMREAD_COLOR) + return result + finally: + self.mark_instance_idle(instance.port) + + def add_instance(self, wait_ready=30): + port = self._get_random_port() + cmd = [self.exe_path, 'start', '--model=lama', '--device=cpu', '--port', str(port), '--model-dir', self.model_dir] + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + instance = self.ServerInstance(port, proc) + self.instances.append(instance) + if self.wait_for_server_ready(port, timeout=wait_ready): + self.logger.log(f"IOPaint 인스턴스 {instance.port} 시작", level=logging.INFO) + else: + self.logger.log(f"IOPaint 인스턴스 {instance.port} 시작 실패", level=logging.ERROR) + return instance + + + +# if __name__ == '__main__': +# manager = IOPaintManager(num_instances=1) +# # result = manager.inpaint(image, mask) # 자동으로 idle 인스턴스에 요청 +# print(manager.get_instance_info()) +# manager.shutdown_all() \ No newline at end of file diff --git a/modules/local_image_server.py b/modules/local_image_server.py new file mode 100644 index 0000000..caad46c --- /dev/null +++ b/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/loggerModule.py b/modules/loggerModule.py new file mode 100644 index 0000000..2531f00 --- /dev/null +++ b/modules/loggerModule.py @@ -0,0 +1,156 @@ +import logging +from logging.handlers import RotatingFileHandler, BaseRotatingHandler +import os +import glob +import traceback +import inspect + + +class Logger1(): + + def __init__(self, log_file="ITServer.log", logger_name="MainLogger", + file_log_level=logging.DEBUG): + """ + Logger 초기화 + :param log_file: 로그 파일 이름 + :param logger_name: 로거 이름 + :param file_log_level: 파일 로거의 로그 레벨 + """ + super().__init__() + self.file_log_level = file_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) + + 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) + +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/mask_module.py b/modules/mask_module.py new file mode 100644 index 0000000..90ddd79 --- /dev/null +++ b/modules/mask_module.py @@ -0,0 +1,44 @@ + +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 = 10, blur_size: int = 15, 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'] + expanded_poly = self.expand_polygon(polygon, offset=5) + cv2.fillPoly(mask, [expanded_poly], 255) + processed_mask = self.process_mask(mask, expansion_size, blur_size) + 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 + \ No newline at end of file diff --git a/modules/ocr_module.py b/modules/ocr_module.py new file mode 100644 index 0000000..18a93db --- /dev/null +++ b/modules/ocr_module.py @@ -0,0 +1,326 @@ +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): + self.logger = logger + self.base_dir = base_dir + + # CPU만 사용하도록 환경 변수 설정 + os.environ['CUDA_VISIBLE_DEVICES'] = '' + + self.ocr = None + + self.ocr = self.initialize_ocr() + if self.ocr is None: + raise Exception("PaddleOCR 초기화 실패") + + def initialize_ocr(self): + """ + PaddleOCR 초기화. det_enabled 옵션에 따라 Detection 모델 사용 여부 결정. + """ + # 모델 디렉토리 설정 + 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 paddleocr import PaddleOCR + + 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 + ) + return ocr + except Exception as e: + self.logger.log(f"❌ PaddleOCR 초기화 실패: {e}", level=logging.ERROR, exc_info=True) + # raise e # 에러 발생시 프로그램 종료 + return None + + + def detect_text(self, image_path: str, method: str = 'polygon') -> 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) + return [] + + try: + # 이미지 읽기 + image = cv2.imread(image_path) + if image is None: + self.logger.log(f"이미지를 읽을 수 없습니다: {image_path}", level=logging.ERROR) + return [] + + self.logger.log(f"🔍 OCR 감지 방식: {method}", level=logging.INFO) + + # 실제 OCR 실행 + # ocr_raw_results = self.ocr.predict(image) + ocr_raw_results = self.ocr.ocr(image) + + 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) + + if not ocr_raw_results or len(ocr_raw_results) == 0: + self.logger.log("⚠️ OCR 결과가 비어있습니다.", level=logging.WARNING) + return [] + + # 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) + return [] + + 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 _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/postImageManager.py b/modules/postImageManager.py new file mode 100644 index 0000000..daca8c8 --- /dev/null +++ b/modules/postImageManager.py @@ -0,0 +1,160 @@ +from PIL import Image, ImageFont, ImageDraw +import requests +import numpy as np +import cv2 +import os +from datetime import datetime +import logging + +class PostImageManager: + def __init__(self, logger, font_path): + self.logger = logger + self.font_path = font_path + + # 폰트 로드 + self.font_load() + + + def font_load(self): + # 폰트 로드 + try: + self.font = ImageFont.truetype(self.font_path, 36) + 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: + 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_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: + if isinstance(image_data, np.ndarray): + image_data = Image.fromarray(cv2.cvtColor(image_data, cv2.COLOR_BGR2RGB)) + + # 폰트가 로드되지 않은 경우 원본 이미지 반환 + if self.font is None: + self.logger.log("폰트가 로드되지 않아 워터마크를 추가할 수 없습니다. 원본 이미지를 반환합니다.", level=logging.WARNING) + return image_data + + # 이미지 복사본 생성 + watermark_image = image_data.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] + + # 이미지 크기 + width, height = image_data.size + + # 워터마크 레이어 생성 + watermark_layer = Image.new("RGBA", (width, height)) # RGBA 이미지 생성 + + # 지그재그 간격 설정 + zigzag_step = int(text_height * 2) # Y축의 지그재그 간격 + + + # 이미지 전체에 반복적으로 워터마크 텍스트 그리기 (지그재그 형태) + for y in range(0, height, zigzag_step): + for x in range(0, width, int(text_width * 3)): # 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") + + except Exception as e: + self.logger.log(f"워터마크 추가 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) + return image_data + + 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/settings_manager.py b/modules/settings_manager.py new file mode 100644 index 0000000..3c55b7d --- /dev/null +++ b/modules/settings_manager.py @@ -0,0 +1,260 @@ +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}") + except Exception as e: + self._log(f"[SettingsManager] {key} 값 저장 중 오류: {e}", level=logging.WARNING) + + # 기타 값 직접 접근용 + 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.INFO) + 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.INFO) + 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.info("[SettingsManager] 저장된 모든 설정값:") + for key in all_keys: + value = self.settings.value(key) + self.logger.info(f" {key}: {value}") + except Exception as e: + self._log(f"[SettingsManager] 설정값 출력 실패: {e}", logging.ERROR, exc_info=True) + + def apply_settings_to_ui(self, widget_obj): + """ + 종속 딕셔너리(toggle_dependencies)에 따라, + 토글/체크박스 등 상태에 따라 dependents/visible 위젯을 자동으로 활성/비활성, 표시/숨김 처리합니다. + + :param widget_obj: 실제 위젯 객체(self 등) + """ + 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) + except Exception as e: + self._log(f"[SettingsManager] {dep} setEnabled 실패: {e}", logging.ERROR, exc_info=True) + # 종속 위젯 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) + except Exception as e: + self._log(f"[SettingsManager] {vis} setVisible 실패: {e}", logging.ERROR, exc_info=True) + + # (필요 시) 확장: 토글에 따라 특정 값을 리셋, 콜백 트리거 등 + + + + + + + + + + + + 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/test_img.py b/modules/test_img.py new file mode 100644 index 0000000..8f890c4 --- /dev/null +++ b/modules/test_img.py @@ -0,0 +1,134 @@ +import os +import sys +import asyncio +import shutil +from modules.image_processor2 import ImageProcessor +from modules.loggerModule import Logger1 +from modules.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 + logger = DummyLogger() + 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_translate_api.py b/modules/test_translate_api.py new file mode 100644 index 0000000..79578c8 --- /dev/null +++ b/modules/test_translate_api.py @@ -0,0 +1,22 @@ +import requests +import json + +API_URL = "http://127.0.0.1:7000/translate_image" + +payload = { + "local_image_path": "d:/py/IT_Server/modules/img/6.jpg", + "file_prefix": "test", + "toggle_states": {"ocr": True}, + "unwanted_texts": {"크리스탈": "크리미"}, + "watermark_text": "테스트 워터마크", + "watermark_opacity": 0.5, + "watermark_font_size": 32 +} + +headers = {"Content-Type": "application/json"} + +response = requests.post(API_URL, data=json.dumps(payload), headers=headers) + +print("응답 결과:") +print(response.status_code) +print(response.json()) \ No newline at end of file diff --git a/modules/test_translate_api_multi.py b/modules/test_translate_api_multi.py new file mode 100644 index 0000000..ac578b6 --- /dev/null +++ b/modules/test_translate_api_multi.py @@ -0,0 +1,32 @@ +import requests +import json +import os +import time + +API_URL = "http://127.0.0.1:7000/translate_images" +IMG_DIR = "d:/py/IT_Server/modules/img" + +# img 폴더의 모든 이미지 파일 리스트업 +image_files = [os.path.join(IMG_DIR, f) for f in os.listdir(IMG_DIR) + if f.lower().endswith((".jpg", ".jpeg", ".png", ".bmp", ".gif", ".webp", ".tif", ".tiff"))] + +payload = { + "local_image_paths": image_files, + "file_prefix": "multi", + "toggle_states": {"ocr": True}, + "unwanted_texts": {"크리스탈": "크리미", "세탁기": "이미지삭제"}, + "watermark_text": "테스트 워터마크", + "watermark_opacity": 0.5, + "watermark_font_size": 32 +} + +headers = {"Content-Type": "application/json"} + +start = time.time() +response = requests.post(API_URL, data=json.dumps(payload), headers=headers) +elapsed = time.time() - start + +print("응답 결과:") +print(response.status_code) +print(response.json()) +print(f"총 소요 시간: {elapsed:.2f}초") \ No newline at end of file diff --git a/modules/text_rendering_module.py b/modules/text_rendering_module.py new file mode 100644 index 0000000..d973a4e --- /dev/null +++ b/modules/text_rendering_module.py @@ -0,0 +1,252 @@ +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 logging + +class TextRenderingModule: + def __init__(self, logger, font_path: Optional[str] = None): + self.logger = logger + self.font_path = font_path + self.default_font_size = 20 + self.font_cache = {} + + 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] + mean_color = np.mean(region, axis=(0, 1)) + 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) + print(f"render_text_on_image font: {font}") + 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) \ No newline at end of file