누끼 서버 기능 추가 및 로컬 배경 제거 모듈 설정을 위한 코드 수정. 이미지 프로세서에서 로컬 모듈 사용 여부와 모델명을 설정할 수 있도록 개선하였으며, 요청 처리 로직에서 서버 상태에 따라 백업 모듈을 사용할 수 있는 기능을 추가하였습니다. 관련 UI 요소도 업데이트하여 사용자 편의성을 향상시켰습니다.
This commit is contained in:
parent
b5f6928bb9
commit
df7f0b80c1
|
|
@ -0,0 +1,328 @@
|
|||
; AutoPercenty3 Inno Setup Script
|
||||
; 이 스크립트는 cx_Freeze로 빌드된 결과물이 있는 "build\exe.win-amd64-3.11" 폴더를 기반으로 인스톨러를 제작합니다.
|
||||
; 20250821_172944에 생성됨
|
||||
|
||||
#define AppId "autopercenty"
|
||||
#define MyAppName "Edit_PartTimer"
|
||||
#define MyAppVersion "3.11.0"
|
||||
#define MyAppPublisher "WhenRideMyCar"
|
||||
#define MyAppProgramName "편집알바생"
|
||||
#define MyAppDescription "편집알바생"
|
||||
#define MyAppCopyright "Copyright 2024"
|
||||
#define MyAppExeName "Edit_PartTimer3"
|
||||
#define MySetupName "Edit_PartTimer Setup"
|
||||
#define MySetupIcon "src/Edit_PartTimer3.ico"
|
||||
#define MySetupOutputDir "dist/installer"
|
||||
|
||||
[Setup]
|
||||
; 기본 설정
|
||||
AppId={#AppId}
|
||||
AppName={#MyAppProgramName}
|
||||
AppVersion={#MyAppVersion}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
DefaultDirName={autopf}\{#MyAppName}
|
||||
DefaultGroupName={#MyAppPublisher}
|
||||
OutputDir={#MySetupOutputDir}
|
||||
OutputBaseFilename={#MySetupName}
|
||||
SetupIconFile={#MySetupIcon}
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
|
||||
; 업데이트 관련 설정 - 권한 최적화
|
||||
PrivilegesRequired=admin
|
||||
PrivilegesRequiredOverridesAllowed=dialog
|
||||
UpdateUninstallLogAppName=yes
|
||||
AppMutex={#MyAppName}
|
||||
CloseApplications=yes
|
||||
RestartApplications=no
|
||||
|
||||
; 보안 및 호환성 설정
|
||||
ArchitecturesAllowed=x64
|
||||
ArchitecturesInstallIn64BitMode=x64
|
||||
AllowNoIcons=yes
|
||||
|
||||
; 버전 정보
|
||||
VersionInfoVersion={#MyAppVersion}
|
||||
VersionInfoCompany={#MyAppPublisher}
|
||||
VersionInfoDescription={#MyAppDescription}
|
||||
VersionInfoCopyright={#MyAppCopyright}
|
||||
VersionInfoProductName={#MyAppProgramName}
|
||||
VersionInfoProductVersion={#MyAppVersion}
|
||||
|
||||
[Languages]
|
||||
Name: "korean"; MessagesFile: "compiler:Languages\Korean.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"
|
||||
|
||||
[Dirs]
|
||||
; 설치 시 {app}\logs 폴더를 생성하고,
|
||||
; Users 그룹에 'modify' 권한(=쓰기 가능)을 부여
|
||||
Name: "{app}\logs"; Permissions: users-modify
|
||||
; 설치 시 {app}\user_data 폴더를 생성하고,
|
||||
; Users 그룹에 'modify' 권한(=쓰기 가능)을 부여
|
||||
Name: "{app}\user_data"; Permissions: users-modify
|
||||
; Playwright 브라우저 폴더를 Program Files 내부에 생성
|
||||
Name: "{app}\lib\src\browsers\chromium-1155"; Permissions: users-modify
|
||||
; Playwright 브라우저 사용자폴더를 Program Files 내부에 생성
|
||||
Name: "{app}\lib\src\browsers\user_data"; Permissions: users-modify
|
||||
|
||||
[Files]
|
||||
; 프로그램 파일만 설치 (항상 덮어쓰기)
|
||||
Source: "build\exe.win-amd64-3.11\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
; VC++ 재배포 패키지 파일을 임시 폴더({tmp})에 복사
|
||||
Source: "VC_redist.x64.exe"; DestDir: "{tmp}"; Flags: deleteafterinstall
|
||||
|
||||
[Registry]
|
||||
; Playwright 브라우저 경로를 Program Files 내부로 설정
|
||||
Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "PLAYWRIGHT_BROWSERS_PATH"; ValueData: "{app}\lib\src\browsers"; Flags: preservestringtype
|
||||
|
||||
[Icons]
|
||||
; 시작 메뉴 바로가기
|
||||
Name: "{group}\{#MyAppProgramName}"; Filename: "{app}\{#MyAppExeName}.exe"
|
||||
; 바탕화면 바로가기
|
||||
Name: "{autodesktop}\{#MyAppProgramName}"; Filename: "{app}\{#MyAppExeName}.exe"; Tasks: desktopicon
|
||||
; 프로그램 제거 바로가기
|
||||
Name: "{group}\{#MyAppProgramName} 제거"; Filename: "{uninstallexe}"
|
||||
|
||||
[Run]
|
||||
; VC++ 재배포 패키지 설치 (필요할 경우)
|
||||
Filename: "{tmp}\VC_redist.x64.exe"; Parameters: "/install /passive /norestart"; StatusMsg: "VC++ 재배포 패키지 설치 중..."; Check: NeedsVCredist
|
||||
; 설치 후 프로그램 실행 (원할 경우)
|
||||
Filename: "{app}\{#MyAppExeName}.exe"; Description: "{cm:LaunchProgram,{#MyAppProgramName}}"; Flags: nowait postinstall skipifsilent
|
||||
|
||||
[Code]
|
||||
function CompareVersion(V1, V2: string): Integer;
|
||||
var
|
||||
P1, P2, N1, N2: Integer;
|
||||
begin
|
||||
P1 := 1;
|
||||
P2 := 1;
|
||||
Result := 0;
|
||||
while (Result = 0) and ((P1 <= Length(V1)) or (P2 <= Length(V2))) do begin
|
||||
while (P1 <= Length(V1)) and (V1[P1] = '.') do Inc(P1);
|
||||
while (P2 <= Length(V2)) and (V2[P2] = '.') do Inc(P2);
|
||||
if (P1 <= Length(V1)) and (P2 <= Length(V2)) then begin
|
||||
N1 := 0; while (P1 <= Length(V1)) and (V1[P1] >= '0') and (V1[P1] <= '9') do begin N1 := N1 * 10 + Ord(V1[P1]) - Ord('0'); Inc(P1); end;
|
||||
N2 := 0; while (P2 <= Length(V2)) and (V2[P2] >= '0') and (V2[P2] <= '9') do begin N2 := N2 * 10 + Ord(V2[P2]) - Ord('0'); Inc(P2); end;
|
||||
if N1 < N2 then Result := -1 else if N1 > N2 then Result := 1;
|
||||
end else begin
|
||||
if P1 <= Length(V1) then Result := 1 else if P2 <= Length(V2) then Result := -1;
|
||||
end;
|
||||
while (P1 <= Length(V1)) and (V1[P1] <> '.') do Inc(P1);
|
||||
while (P2 <= Length(V2)) and (V2[P2] <> '.') do Inc(P2);
|
||||
end;
|
||||
end;
|
||||
|
||||
// 파일 또는 폴더 복사 함수
|
||||
procedure CopyDir(const SourcePath, DestPath: string);
|
||||
var
|
||||
FindRec: TFindRec;
|
||||
SourceFilePath: string;
|
||||
DestFilePath: string;
|
||||
begin
|
||||
ForceDirectories(DestPath);
|
||||
|
||||
if FindFirst(SourcePath + '\*', FindRec) then
|
||||
begin
|
||||
try
|
||||
repeat
|
||||
if (FindRec.Name <> '.') and (FindRec.Name <> '..') then
|
||||
begin
|
||||
SourceFilePath := SourcePath + '\' + FindRec.Name;
|
||||
DestFilePath := DestPath + '\' + FindRec.Name;
|
||||
|
||||
if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY = 0 then
|
||||
begin
|
||||
if FileCopy(SourceFilePath, DestFilePath, False) then
|
||||
Log('파일 복사 성공: ' + SourceFilePath + ' -> ' + DestFilePath)
|
||||
else
|
||||
Log('파일 복사 실패: ' + SourceFilePath);
|
||||
end
|
||||
else
|
||||
CopyDir(SourceFilePath, DestFilePath);
|
||||
end;
|
||||
until not FindNext(FindRec);
|
||||
finally
|
||||
FindClose(FindRec);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// 디렉토리 삭제 함수
|
||||
procedure DeleteDir(const DirPath: string);
|
||||
var
|
||||
FindRec: TFindRec;
|
||||
FilePath: string;
|
||||
begin
|
||||
if not DirExists(DirPath) then Exit;
|
||||
|
||||
if FindFirst(DirPath + '\*', FindRec) then
|
||||
begin
|
||||
try
|
||||
repeat
|
||||
if (FindRec.Name <> '.') and (FindRec.Name <> '..') then
|
||||
begin
|
||||
FilePath := DirPath + '\' + FindRec.Name;
|
||||
|
||||
if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY = 0 then
|
||||
begin
|
||||
if DeleteFile(FilePath) then
|
||||
Log('파일 삭제 성공: ' + FilePath)
|
||||
else
|
||||
Log('파일 삭제 실패: ' + FilePath);
|
||||
end
|
||||
else
|
||||
DeleteDir(FilePath);
|
||||
end;
|
||||
until not FindNext(FindRec);
|
||||
finally
|
||||
FindClose(FindRec);
|
||||
end;
|
||||
end;
|
||||
|
||||
if RemoveDir(DirPath) then
|
||||
Log('디렉토리 삭제 성공: ' + DirPath)
|
||||
else
|
||||
Log('디렉토리 삭제 실패: ' + DirPath);
|
||||
end;
|
||||
|
||||
// 프로그램 실행 여부 확인
|
||||
function IsAppRunning(const FileName: string): Boolean;
|
||||
var
|
||||
Handle: THandle;
|
||||
begin
|
||||
Handle := FindWindowByWindowName('{#MyAppProgramName}'); // 프로그램의 윈도우 타이틀로 찾기
|
||||
Result := (Handle <> 0);
|
||||
end;
|
||||
|
||||
// 프로그램 종료
|
||||
procedure CloseApplication(const FileName: string);
|
||||
var
|
||||
Handle: THandle;
|
||||
begin
|
||||
Handle := FindWindowByWindowName('{#MyAppProgramName}');
|
||||
if Handle <> 0 then
|
||||
begin
|
||||
PostMessage(Handle, 18, 0, 0); // WM_QUIT
|
||||
Sleep(1000); // 종료 대기
|
||||
end;
|
||||
end;
|
||||
|
||||
// VC++ 재배포 패키지 필요 여부 확인
|
||||
function NeedsVCredist: Boolean;
|
||||
begin
|
||||
if RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64') then
|
||||
Result := False // 이미 설치됨
|
||||
else
|
||||
Result := True; // 미설치 -> 설치 필요
|
||||
end;
|
||||
|
||||
// 설치 완료 후 실행 여부 확인
|
||||
function InitializeFinish(): Boolean;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Result := True;
|
||||
if MsgBox('설치가 완료되었습니다. 프로그램을 실행하시겠습니까?' + #13#10 +
|
||||
'(실행 시 서버와 동기화하여 설정이 업데이트됩니다)',
|
||||
mbConfirmation, MB_YESNO) = IDYES then
|
||||
begin
|
||||
Exec(ExpandConstant('{app}\{#MyAppExeName}.exe'), '', '', SW_SHOW, ewNoWait, ResultCode);
|
||||
end;
|
||||
end;
|
||||
|
||||
function InitializeSetup(): Boolean;
|
||||
var
|
||||
OldVersion: String;
|
||||
NewVersion: String;
|
||||
OldAppPath: String;
|
||||
UserDataSourcePath, UserDataBackupPath: String;
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Result := True;
|
||||
NewVersion := '{#MyAppVersion}';
|
||||
UserDataBackupPath := ExpandConstant('{tmp}\user_data_backup');
|
||||
|
||||
// 현재 프로그램 버전 확인
|
||||
if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#MyAppName}_is1',
|
||||
'DisplayVersion', OldVersion) then
|
||||
begin
|
||||
// 같은 버전이거나 더 높은 버전이 설치되어 있는 경우
|
||||
if CompareVersion(OldVersion, NewVersion) >= 0 then
|
||||
begin
|
||||
MsgBox('현재 설치된 버전(' + OldVersion + ')이 이 설치 프로그램의 버전(' +
|
||||
NewVersion + ')과 같거나 더 높습니다.' + #13#10 +
|
||||
'설치를 계속할 수 없습니다.', mbInformation, MB_OK);
|
||||
Result := False;
|
||||
exit;
|
||||
end;
|
||||
|
||||
// 이전 버전이 설치되어 있는 경우 업데이트 진행
|
||||
if CompareVersion(OldVersion, NewVersion) < 0 then
|
||||
begin
|
||||
Log('업데이트 설치 진행: ' + OldVersion + ' -> ' + NewVersion);
|
||||
|
||||
// 프로그램이 실행 중인지 확인하고 종료 요청
|
||||
if IsAppRunning('{#MyAppExeName}.exe') then
|
||||
begin
|
||||
if MsgBox('프로그램을 업데이트하기 위해 실행 중인 프로그램을 종료해야 합니다.' + #13#10 +
|
||||
'계속하시겠습니까?', mbConfirmation, MB_YESNO) = IDNO then
|
||||
begin
|
||||
Result := False;
|
||||
exit;
|
||||
end;
|
||||
CloseApplication('{#MyAppExeName}.exe');
|
||||
Sleep(2000); // 프로세스 종료 대기
|
||||
end;
|
||||
|
||||
// 레지스트리에서 설치 경로 확인
|
||||
if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#MyAppName}_is1',
|
||||
'InstallLocation', OldAppPath) then
|
||||
begin
|
||||
Log('기존 설치 경로: ' + OldAppPath);
|
||||
|
||||
// lib/src/user_data 폴더 백업
|
||||
UserDataSourcePath := OldAppPath + '\lib\src\user_data';
|
||||
if DirExists(UserDataSourcePath) then
|
||||
begin
|
||||
Log('사용자 데이터 백업 중: ' + UserDataSourcePath + ' -> ' + UserDataBackupPath);
|
||||
ForceDirectories(UserDataBackupPath);
|
||||
CopyDir(UserDataSourcePath, UserDataBackupPath);
|
||||
end
|
||||
else
|
||||
begin
|
||||
Log('사용자 데이터 폴더가 존재하지 않음: ' + UserDataSourcePath);
|
||||
end;
|
||||
|
||||
// 기존 설치 폴더 완전 삭제
|
||||
if DirExists(OldAppPath) then
|
||||
begin
|
||||
Log('기존 설치 폴더 삭제 중: ' + OldAppPath);
|
||||
DeleteDir(OldAppPath);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
var
|
||||
UserDataBackupPath, UserDataDestPath: String;
|
||||
begin
|
||||
// 설치 완료 후
|
||||
if CurStep = ssPostInstall then
|
||||
begin
|
||||
UserDataBackupPath := ExpandConstant('{tmp}\user_data_backup');
|
||||
UserDataDestPath := ExpandConstant('{app}\lib\src\user_data');
|
||||
|
||||
// 백업한 사용자 데이터 폴더가 있으면 복원
|
||||
if DirExists(UserDataBackupPath) then
|
||||
begin
|
||||
Log('사용자 데이터 복원 중: ' + UserDataBackupPath + ' -> ' + UserDataDestPath);
|
||||
ForceDirectories(UserDataDestPath);
|
||||
CopyDir(UserDataBackupPath, UserDataDestPath);
|
||||
Log('사용자 데이터 복원 완료');
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
26
mainUI_SP.py
26
mainUI_SP.py
|
|
@ -1178,6 +1178,9 @@ class MAIN_GUI(QMainWindow):
|
|||
'image_worker_restart_every': 10,
|
||||
'image_worker_restart_count': 0,
|
||||
'products_per_context_restart': 20,
|
||||
|
||||
'use_local_rembg': False,
|
||||
'local_model_name': "birefnet-general-lite",
|
||||
|
||||
'output_image_format': "webp",
|
||||
"inpaint_method": "lama:cuda",
|
||||
|
|
@ -4005,6 +4008,29 @@ class MAIN_GUI(QMainWindow):
|
|||
self.thumb_rmb_layout.addWidget(self.thumb_rmb_count_input)
|
||||
|
||||
self.thumbnail_toggle_layout.addWidget(self.thumb_rmb_widget)
|
||||
|
||||
# 누끼 서버
|
||||
self.nukki_server_widget = QWidget()
|
||||
self.nukki_server_toggle_layout = QHBoxLayout(self.nukki_server_widget)
|
||||
self.nukki_server_toggle_label = QLabel("누끼 서버", self)
|
||||
self.nukki_server_toggle = ToggleSwitch(self)
|
||||
self.nukki_server_toggle.setObjectName("nukki_server_toggle")
|
||||
self.nukki_server_toggle.clicked.connect(lambda checked: self.universal_input_handler(self.nukki_server_toggle, checked))
|
||||
self.nukki_server_widget.enterEvent = lambda e: self.show_manual_html(
|
||||
self.thumbnail_manual_group,
|
||||
"✂️ 누끼 서버",
|
||||
self.thumbnail_manual_label,
|
||||
"""
|
||||
<p>누끼 서버가 죽었을때 백업동작 설정을 사용할지 여부를 결정합니다.</p>
|
||||
"""
|
||||
)
|
||||
self.nukki_server_widget.leaveEvent = lambda e: self.reset_manual(self.thumbnail_manual_group, self.thumbnail_manual_label)
|
||||
|
||||
self.nukki_server_toggle_layout.addWidget(self.nukki_server_toggle_label)
|
||||
self.nukki_server_toggle_layout.addWidget(self.nukki_server_toggle)
|
||||
|
||||
self.thumbnail_toggle_layout.addWidget(self.nukki_server_widget)
|
||||
|
||||
|
||||
# 레이아웃에 그룹 추가
|
||||
self.thumbnail_layout.addWidget(self.thumbnail_toggle_group, 3)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,188 @@
|
|||
'\" -*- coding: us-ascii -*-
|
||||
.if \n(.g .ds T< \\FC
|
||||
.if \n(.g .ds T> \\F[\n[.fam]]
|
||||
.de URL
|
||||
\\$2 \(la\\$1\(ra\\$3
|
||||
..
|
||||
.if \n(.g .mso www.tmac
|
||||
.TH isympy 1 2007-10-8 "" ""
|
||||
.SH NAME
|
||||
isympy \- interactive shell for SymPy
|
||||
.SH SYNOPSIS
|
||||
'nh
|
||||
.fi
|
||||
.ad l
|
||||
\fBisympy\fR \kx
|
||||
.if (\nx>(\n(.l/2)) .nr x (\n(.l/5)
|
||||
'in \n(.iu+\nxu
|
||||
[\fB-c\fR | \fB--console\fR] [\fB-p\fR ENCODING | \fB--pretty\fR ENCODING] [\fB-t\fR TYPE | \fB--types\fR TYPE] [\fB-o\fR ORDER | \fB--order\fR ORDER] [\fB-q\fR | \fB--quiet\fR] [\fB-d\fR | \fB--doctest\fR] [\fB-C\fR | \fB--no-cache\fR] [\fB-a\fR | \fB--auto\fR] [\fB-D\fR | \fB--debug\fR] [
|
||||
-- | PYTHONOPTIONS]
|
||||
'in \n(.iu-\nxu
|
||||
.ad b
|
||||
'hy
|
||||
'nh
|
||||
.fi
|
||||
.ad l
|
||||
\fBisympy\fR \kx
|
||||
.if (\nx>(\n(.l/2)) .nr x (\n(.l/5)
|
||||
'in \n(.iu+\nxu
|
||||
[
|
||||
{\fB-h\fR | \fB--help\fR}
|
||||
|
|
||||
{\fB-v\fR | \fB--version\fR}
|
||||
]
|
||||
'in \n(.iu-\nxu
|
||||
.ad b
|
||||
'hy
|
||||
.SH DESCRIPTION
|
||||
isympy is a Python shell for SymPy. It is just a normal python shell
|
||||
(ipython shell if you have the ipython package installed) that executes
|
||||
the following commands so that you don't have to:
|
||||
.PP
|
||||
.nf
|
||||
\*(T<
|
||||
>>> from __future__ import division
|
||||
>>> from sympy import *
|
||||
>>> x, y, z = symbols("x,y,z")
|
||||
>>> k, m, n = symbols("k,m,n", integer=True)
|
||||
\*(T>
|
||||
.fi
|
||||
.PP
|
||||
So starting isympy is equivalent to starting python (or ipython) and
|
||||
executing the above commands by hand. It is intended for easy and quick
|
||||
experimentation with SymPy. For more complicated programs, it is recommended
|
||||
to write a script and import things explicitly (using the "from sympy
|
||||
import sin, log, Symbol, ..." idiom).
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\*(T<\fB\-c \fR\*(T>\fISHELL\fR, \*(T<\fB\-\-console=\fR\*(T>\fISHELL\fR
|
||||
Use the specified shell (python or ipython) as
|
||||
console backend instead of the default one (ipython
|
||||
if present or python otherwise).
|
||||
|
||||
Example: isympy -c python
|
||||
|
||||
\fISHELL\fR could be either
|
||||
\&'ipython' or 'python'
|
||||
.TP
|
||||
\*(T<\fB\-p \fR\*(T>\fIENCODING\fR, \*(T<\fB\-\-pretty=\fR\*(T>\fIENCODING\fR
|
||||
Setup pretty printing in SymPy. By default, the most pretty, unicode
|
||||
printing is enabled (if the terminal supports it). You can use less
|
||||
pretty ASCII printing instead or no pretty printing at all.
|
||||
|
||||
Example: isympy -p no
|
||||
|
||||
\fIENCODING\fR must be one of 'unicode',
|
||||
\&'ascii' or 'no'.
|
||||
.TP
|
||||
\*(T<\fB\-t \fR\*(T>\fITYPE\fR, \*(T<\fB\-\-types=\fR\*(T>\fITYPE\fR
|
||||
Setup the ground types for the polys. By default, gmpy ground types
|
||||
are used if gmpy2 or gmpy is installed, otherwise it falls back to python
|
||||
ground types, which are a little bit slower. You can manually
|
||||
choose python ground types even if gmpy is installed (e.g., for testing purposes).
|
||||
|
||||
Note that sympy ground types are not supported, and should be used
|
||||
only for experimental purposes.
|
||||
|
||||
Note that the gmpy1 ground type is primarily intended for testing; it the
|
||||
use of gmpy even if gmpy2 is available.
|
||||
|
||||
This is the same as setting the environment variable
|
||||
SYMPY_GROUND_TYPES to the given ground type (e.g.,
|
||||
SYMPY_GROUND_TYPES='gmpy')
|
||||
|
||||
The ground types can be determined interactively from the variable
|
||||
sympy.polys.domains.GROUND_TYPES inside the isympy shell itself.
|
||||
|
||||
Example: isympy -t python
|
||||
|
||||
\fITYPE\fR must be one of 'gmpy',
|
||||
\&'gmpy1' or 'python'.
|
||||
.TP
|
||||
\*(T<\fB\-o \fR\*(T>\fIORDER\fR, \*(T<\fB\-\-order=\fR\*(T>\fIORDER\fR
|
||||
Setup the ordering of terms for printing. The default is lex, which
|
||||
orders terms lexicographically (e.g., x**2 + x + 1). You can choose
|
||||
other orderings, such as rev-lex, which will use reverse
|
||||
lexicographic ordering (e.g., 1 + x + x**2).
|
||||
|
||||
Note that for very large expressions, ORDER='none' may speed up
|
||||
printing considerably, with the tradeoff that the order of the terms
|
||||
in the printed expression will have no canonical order
|
||||
|
||||
Example: isympy -o rev-lax
|
||||
|
||||
\fIORDER\fR must be one of 'lex', 'rev-lex', 'grlex',
|
||||
\&'rev-grlex', 'grevlex', 'rev-grevlex', 'old', or 'none'.
|
||||
.TP
|
||||
\*(T<\fB\-q\fR\*(T>, \*(T<\fB\-\-quiet\fR\*(T>
|
||||
Print only Python's and SymPy's versions to stdout at startup, and nothing else.
|
||||
.TP
|
||||
\*(T<\fB\-d\fR\*(T>, \*(T<\fB\-\-doctest\fR\*(T>
|
||||
Use the same format that should be used for doctests. This is
|
||||
equivalent to '\fIisympy -c python -p no\fR'.
|
||||
.TP
|
||||
\*(T<\fB\-C\fR\*(T>, \*(T<\fB\-\-no\-cache\fR\*(T>
|
||||
Disable the caching mechanism. Disabling the cache may slow certain
|
||||
operations down considerably. This is useful for testing the cache,
|
||||
or for benchmarking, as the cache can result in deceptive benchmark timings.
|
||||
|
||||
This is the same as setting the environment variable SYMPY_USE_CACHE
|
||||
to 'no'.
|
||||
.TP
|
||||
\*(T<\fB\-a\fR\*(T>, \*(T<\fB\-\-auto\fR\*(T>
|
||||
Automatically create missing symbols. Normally, typing a name of a
|
||||
Symbol that has not been instantiated first would raise NameError,
|
||||
but with this option enabled, any undefined name will be
|
||||
automatically created as a Symbol. This only works in IPython 0.11.
|
||||
|
||||
Note that this is intended only for interactive, calculator style
|
||||
usage. In a script that uses SymPy, Symbols should be instantiated
|
||||
at the top, so that it's clear what they are.
|
||||
|
||||
This will not override any names that are already defined, which
|
||||
includes the single character letters represented by the mnemonic
|
||||
QCOSINE (see the "Gotchas and Pitfalls" document in the
|
||||
documentation). You can delete existing names by executing "del
|
||||
name" in the shell itself. You can see if a name is defined by typing
|
||||
"'name' in globals()".
|
||||
|
||||
The Symbols that are created using this have default assumptions.
|
||||
If you want to place assumptions on symbols, you should create them
|
||||
using symbols() or var().
|
||||
|
||||
Finally, this only works in the top level namespace. So, for
|
||||
example, if you define a function in isympy with an undefined
|
||||
Symbol, it will not work.
|
||||
.TP
|
||||
\*(T<\fB\-D\fR\*(T>, \*(T<\fB\-\-debug\fR\*(T>
|
||||
Enable debugging output. This is the same as setting the
|
||||
environment variable SYMPY_DEBUG to 'True'. The debug status is set
|
||||
in the variable SYMPY_DEBUG within isympy.
|
||||
.TP
|
||||
-- \fIPYTHONOPTIONS\fR
|
||||
These options will be passed on to \fIipython (1)\fR shell.
|
||||
Only supported when ipython is being used (standard python shell not supported).
|
||||
|
||||
Two dashes (--) are required to separate \fIPYTHONOPTIONS\fR
|
||||
from the other isympy options.
|
||||
|
||||
For example, to run iSymPy without startup banner and colors:
|
||||
|
||||
isympy -q -c ipython -- --colors=NoColor
|
||||
.TP
|
||||
\*(T<\fB\-h\fR\*(T>, \*(T<\fB\-\-help\fR\*(T>
|
||||
Print help output and exit.
|
||||
.TP
|
||||
\*(T<\fB\-v\fR\*(T>, \*(T<\fB\-\-version\fR\*(T>
|
||||
Print isympy version information and exit.
|
||||
.SH FILES
|
||||
.TP
|
||||
\*(T<\fI${HOME}/.sympy\-history\fR\*(T>
|
||||
Saves the history of commands when using the python
|
||||
shell as backend.
|
||||
.SH BUGS
|
||||
The upstreams BTS can be found at \(lahttps://github.com/sympy/sympy/issues\(ra
|
||||
Please report all bugs that you find in there, this will help improve
|
||||
the overall quality of SymPy.
|
||||
.SH "SEE ALSO"
|
||||
\fBipython\fR(1), \fBpython\fR(1)
|
||||
|
|
@ -54,6 +54,11 @@ class ImageProcessor3:
|
|||
|
||||
self.TEMP_IMAGE_DIR = self.toggle_states.get('TEMP_IMAGE_DIR', "")
|
||||
|
||||
self.use_local_rembg = self.toggle_states.get("use_local_rembg", False)
|
||||
|
||||
self.local_model_name = self.toggle_states.get("local_model_name", 'birefnet-general-lite')
|
||||
|
||||
|
||||
self.logger.log(f"self.toggle_states: {self.toggle_states}", level=logging.DEBUG)
|
||||
|
||||
self.logger.log(f"self.font_path: {self.font_path}", level=logging.DEBUG)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import base64
|
|||
import numpy as np
|
||||
import os
|
||||
import logging
|
||||
from PIL import Image
|
||||
|
||||
|
||||
class Request_AI_Server:
|
||||
"""IOPaint 서버 연동 인페인팅 모델 (REST API /api/v1/inpaint 사용, 바이너리 PNG 반환)"""
|
||||
|
|
@ -25,6 +27,21 @@ class Request_AI_Server:
|
|||
self.inpaint_api_url = f"{self.inpaint_base_url}/api/v1/inpaint"
|
||||
# RemoveBG 플러그인은 고정 엔드포인트
|
||||
self.rembg_api_url = f"{self.rembg_base_url}/api/v1/run_plugin_gen_image"
|
||||
|
||||
# 백업용 내장 rembg 모듈 (필요시에만 초기화)
|
||||
self._backup_rembg = None
|
||||
|
||||
def _get_backup_rembg(self):
|
||||
"""백업용 내장 rembg 모듈을 lazy loading으로 초기화"""
|
||||
if self._backup_rembg is None:
|
||||
try:
|
||||
from .background_removal_module import BackgroundRemovalModule
|
||||
self._backup_rembg = BackgroundRemovalModule(logger=self.logger, default_model="u2net")
|
||||
self.logger.log("백업용 내장 rembg 모듈 초기화됨", level=logging.INFO)
|
||||
except ImportError as e:
|
||||
self.logger.log(f"백업용 rembg 모듈 import 실패: {e}", level=logging.ERROR)
|
||||
return None
|
||||
return self._backup_rembg
|
||||
|
||||
def request_inpaint(self, image: np.ndarray, mask: np.ndarray) -> np.ndarray:
|
||||
|
||||
|
|
@ -64,14 +81,16 @@ class Request_AI_Server:
|
|||
self.logger.log(f"인페인팅 서버 에러: {e}", level=logging.ERROR, exc_info=True)
|
||||
return None
|
||||
|
||||
def request_rembg(self, image: np.ndarray) -> np.ndarray:
|
||||
"""RemoveBG 플러그인 호출 후 결과 이미지를 흰 배경 중앙 배치로 후처리."""
|
||||
def request_rembg(self, image: np.ndarray, use_local_rembg: bool = False, local_model_name: str = "birefnet-general-lite") -> np.ndarray:
|
||||
"""RemoveBG 플러그인 호출 후 결과 이미지를 흰 배경 중앙 배치로 후처리.
|
||||
서버가 다운될 경우 백업으로 내장 rembg 모듈 사용.
|
||||
|
||||
Args:
|
||||
image: 입력 이미지 (np.ndarray 또는 파일 경로)
|
||||
use_local_rembg: True면 서버 상태와 관계없이 내장 rembg 모듈 직접 사용
|
||||
local_model_name: 내장 rembg에서 사용할 모델명 (기본값: "birefnet-general-lite")
|
||||
"""
|
||||
try:
|
||||
# 서버 상태 먼저 확인
|
||||
if not self.is_server_alive(self.rembg_base_url):
|
||||
self.logger.log("rembg 서버가 비정상입니다.", level=logging.WARNING)
|
||||
return None
|
||||
|
||||
# 입력 이미지 로드/확정
|
||||
if isinstance(image, str) and os.path.isfile(image):
|
||||
image_data = cv2.imread(image)
|
||||
|
|
@ -81,6 +100,16 @@ class Request_AI_Server:
|
|||
self.logger.log(f"이미지 파일을 읽을 수 없습니다: {image}", level=logging.ERROR)
|
||||
return None
|
||||
|
||||
# 내장 모듈 직접 사용 요청시
|
||||
if use_local_rembg:
|
||||
self.logger.log(f"외부 인자에 의해 내장 rembg 모듈({local_model_name})을 직접 사용합니다.", level=logging.INFO)
|
||||
return self._use_backup_rembg(image_data, local_model_name)
|
||||
|
||||
# 서버 상태 먼저 확인
|
||||
if not self.is_server_alive(self.rembg_base_url):
|
||||
self.logger.log("rembg 서버가 비정상입니다. 백업 내장 rembg 모듈을 사용합니다.", level=logging.WARNING)
|
||||
return self._use_backup_rembg(image_data, local_model_name)
|
||||
|
||||
# base64 인코딩 (data URL)
|
||||
_, img_encoded = cv2.imencode('.png', image_data)
|
||||
img_b64 = base64.b64encode(img_encoded).decode('utf-8')
|
||||
|
|
@ -92,17 +121,77 @@ class Request_AI_Server:
|
|||
|
||||
response = requests.post(self.rembg_api_url, json=payload)
|
||||
if response.status_code != 200:
|
||||
self.logger.log(f"rembg 서버 에러: {response.text}", level=logging.ERROR)
|
||||
return None
|
||||
self.logger.log(f"rembg 서버 에러: {response.text}. 백업 내장 rembg 모듈을 사용합니다.", level=logging.WARNING)
|
||||
return self._use_backup_rembg(image_data, local_model_name)
|
||||
|
||||
# PNG 바이너리 → numpy
|
||||
nparr = np.frombuffer(response.content, np.uint8)
|
||||
result_img = cv2.imdecode(nparr, cv2.IMREAD_UNCHANGED)
|
||||
|
||||
if result_img is None or result_img.ndim != 3:
|
||||
return result_img # 실패 시 원본 반환
|
||||
self.logger.log("서버 응답 이미지 디코딩 실패. 백업 내장 rembg 모듈을 사용합니다.", level=logging.WARNING)
|
||||
return self._use_backup_rembg(image_data, local_model_name)
|
||||
|
||||
# ---- 후처리: 마스크 정제 및 중앙 배치 ----
|
||||
return self._postprocess_rembg_result(result_img)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.log(f"rembg 서버 에러: {e}. 백업 내장 rembg 모듈을 사용합니다.", level=logging.WARNING, exc_info=True)
|
||||
return self._use_backup_rembg(image_data if 'image_data' in locals() else image, local_model_name)
|
||||
|
||||
def _use_backup_rembg(self, image_data: np.ndarray, model_name: str = "birefnet-general-lite") -> np.ndarray:
|
||||
"""내장 rembg 모듈을 사용하여 배경 제거
|
||||
|
||||
Args:
|
||||
image_data: 입력 이미지 데이터
|
||||
model_name: 사용할 rembg 모델명
|
||||
"""
|
||||
try:
|
||||
backup_rembg = self._get_backup_rembg()
|
||||
if backup_rembg is None:
|
||||
self.logger.log("백업 rembg 모듈을 사용할 수 없습니다.", level=logging.ERROR)
|
||||
return None
|
||||
|
||||
# 임시 파일 저장 (BackgroundRemovalModule은 파일 경로를 받음)
|
||||
import tempfile
|
||||
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as temp_file:
|
||||
temp_path = temp_file.name
|
||||
cv2.imwrite(temp_path, image_data)
|
||||
|
||||
try:
|
||||
# 내장 rembg로 배경 제거 (지정된 모델 사용)
|
||||
result_pil = backup_rembg.remove_background(temp_path, model_name=model_name)
|
||||
if result_pil is None:
|
||||
self.logger.log(f"내장 rembg 모듈({model_name}) 처리 실패", level=logging.ERROR)
|
||||
return None
|
||||
|
||||
# PIL Image를 numpy array로 변환
|
||||
if result_pil.mode == 'RGBA':
|
||||
result_rgba = np.array(result_pil)
|
||||
result_img = cv2.cvtColor(result_rgba, cv2.COLOR_RGBA2BGRA)
|
||||
else:
|
||||
result_rgb = np.array(result_pil)
|
||||
result_img = cv2.cvtColor(result_rgb, cv2.COLOR_RGB2BGR)
|
||||
# 알파 채널 추가 (배경 제거된 결과이므로 마스크 생성 필요)
|
||||
gray = cv2.cvtColor(result_img, cv2.COLOR_BGR2GRAY)
|
||||
alpha = np.where(gray > 10, 255, 0).astype(np.uint8)
|
||||
result_img = cv2.merge([result_img[:,:,0], result_img[:,:,1], result_img[:,:,2], alpha])
|
||||
|
||||
# 동일한 후처리 적용
|
||||
return self._postprocess_rembg_result(result_img)
|
||||
|
||||
finally:
|
||||
# 임시 파일 삭제
|
||||
if os.path.exists(temp_path):
|
||||
os.unlink(temp_path)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.log(f"백업 rembg 모듈 에러: {e}", level=logging.ERROR, exc_info=True)
|
||||
return None
|
||||
|
||||
def _postprocess_rembg_result(self, result_img: np.ndarray) -> np.ndarray:
|
||||
"""RemoveBG 결과 이미지 후처리: 마스크 정제 및 중앙 배치"""
|
||||
try:
|
||||
# 1) 초기 마스크
|
||||
if result_img.shape[2] == 4:
|
||||
mask_init = (result_img[:, :, 3] > 200).astype(np.uint8)
|
||||
|
|
@ -149,8 +238,9 @@ class Request_AI_Server:
|
|||
white_bg = np.full_like(bgr_crop, 255.0)
|
||||
final_img = (bgr_crop * alpha_crop + white_bg * (1 - alpha_crop)).astype(np.uint8)
|
||||
return final_img
|
||||
|
||||
except Exception as e:
|
||||
self.logger.log(f"rembg 서버 에러: {e}", level=logging.ERROR, exc_info=True)
|
||||
self.logger.log(f"rembg 결과 후처리 에러: {e}", level=logging.ERROR, exc_info=True)
|
||||
return None
|
||||
|
||||
def is_server_alive(self, base_url: str, timeout: int = 3) -> bool:
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in New Issue