SanskarModi commited on
Commit
9bc957e
·
1 Parent(s): 069200c

updated app

Browse files
.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ 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
31
+ -----END CERTIFICATE-----
requirements.txt CHANGED
@@ -8,15 +8,15 @@ torchaudio==2.5.1
8
 
9
 
10
  # HUGGINGFACE DIFFUSION ECOSYSTEM
11
- diffusers==0.29.2
12
- transformers==4.39.3
13
- huggingface_hub==0.29.1
14
  accelerate==0.28.0
15
- safetensors==0.4.2
16
 
17
 
18
  # UI FRAMEWORK
19
- gradio==3.50.2
20
 
21
 
22
  # IMAGE PROCESSING & UTILITIES
 
8
 
9
 
10
  # HUGGINGFACE DIFFUSION ECOSYSTEM
11
+ diffusers==0.35.2
12
+ transformers==4.57.3
13
+ huggingface_hub==0.36.0
14
  accelerate==0.28.0
15
+ safetensors==0.7.0
16
 
17
 
18
  # UI FRAMEWORK
19
+ gradio==6.0.2
20
 
21
 
22
  # IMAGE PROCESSING & UTILITIES
src/sdgen/config/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
  """Configuration exports for the sdgen package.
2
 
3
  This module re-exports commonly used configuration paths and settings
4
- so they can be imported directly from `src.sdgen.config`.
5
  """
6
 
7
  from __future__ import annotations
 
1
  """Configuration exports for the sdgen package.
2
 
3
  This module re-exports commonly used configuration paths and settings
4
+ so they can be imported directly from `sdgen.config`.
5
  """
6
 
7
  from __future__ import annotations
src/sdgen/config/paths.py CHANGED
@@ -1,4 +1,4 @@
1
- """Path configuration for src.sdgen.
2
 
3
  All filesystem paths are resolved relative to the project root.
4
  The project root is detected by walking upward until a marker
 
1
+ """Path configuration for sdgen.
2
 
3
  All filesystem paths are resolved relative to the project root.
4
  The project root is detected by walking upward until a marker
src/sdgen/config/settings.py CHANGED
@@ -1,4 +1,4 @@
1
- """Application runtime settings for src.sdgen.
2
 
3
  AppSettings reads configuration values from environment variables at
4
  process start and exposes them as strongly typed attributes.
 
1
+ """Application runtime settings for sdgen.
2
 
3
  AppSettings reads configuration values from environment variables at
4
  process start and exposes them as strongly typed attributes.
src/sdgen/main.py CHANGED
@@ -9,11 +9,11 @@ from __future__ import annotations
9
  import torch
10
  from dotenv import load_dotenv
11
 
12
- from src.sdgen.config import AppSettings
13
- from src.sdgen.sd.img2img import prepare_img2img_pipeline
14
- from src.sdgen.sd.pipeline import load_pipeline, warmup_pipeline
15
- from src.sdgen.ui import build_ui
16
- from src.sdgen.utils.logger import get_logger
17
 
18
  logger = get_logger(__name__)
19
  load_dotenv()
@@ -57,7 +57,7 @@ def main() -> None:
57
  ),
58
  }
59
  if device == "cuda" and settings.warmup:
60
- warmup_pipeline(pipes["SD1.5"])
61
 
62
  img2img_pipes = {
63
  "SD1.5": prepare_img2img_pipeline(pipes["SD1.5"]),
@@ -65,10 +65,7 @@ def main() -> None:
65
  }
66
 
67
  demo = build_ui(pipes, img2img_pipes)
68
- demo.queue(
69
- concurrency_count=1,
70
- max_size=8,
71
- ).launch(
72
  server_name=settings.server_host,
73
  server_port=settings.server_port,
74
  share=settings.share,
 
9
  import torch
10
  from dotenv import load_dotenv
11
 
12
+ from sdgen.config import AppSettings
13
+ from sdgen.sd.img2img import prepare_img2img_pipeline
14
+ from sdgen.sd.pipeline import load_pipeline, warmup_pipeline
15
+ from sdgen.ui import build_ui
16
+ from sdgen.utils.logger import get_logger
17
 
18
  logger = get_logger(__name__)
19
  load_dotenv()
 
57
  ),
58
  }
59
  if device == "cuda" and settings.warmup:
60
+ warmup_pipeline(pipes["Turbo"])
61
 
62
  img2img_pipes = {
63
  "SD1.5": prepare_img2img_pipeline(pipes["SD1.5"]),
 
65
  }
66
 
67
  demo = build_ui(pipes, img2img_pipes)
68
+ demo.launch(
 
 
 
69
  server_name=settings.server_host,
70
  server_port=settings.server_port,
71
  share=settings.share,
src/sdgen/sd/generator.py CHANGED
@@ -8,9 +8,9 @@ from typing import Tuple
8
  import torch
9
  from PIL import Image
10
 
11
- from src.sdgen.sd.models import GenerationMetadata, Txt2ImgConfig
12
- from src.sdgen.utils.common import validate_resolution
13
- from src.sdgen.utils.logger import get_logger
14
 
15
  logger = get_logger(__name__)
16
 
 
8
  import torch
9
  from PIL import Image
10
 
11
+ from sdgen.sd.models import GenerationMetadata, Txt2ImgConfig
12
+ from sdgen.utils.common import validate_resolution
13
+ from sdgen.utils.logger import get_logger
14
 
15
  logger = get_logger(__name__)
16
 
src/sdgen/sd/img2img.py CHANGED
@@ -8,9 +8,9 @@ import torch
8
  from diffusers import StableDiffusionImg2ImgPipeline
9
  from PIL import Image
10
 
11
- from src.sdgen.sd.models import GenerationMetadata, Img2ImgConfig
12
- from src.sdgen.utils.common import validate_resolution
13
- from src.sdgen.utils.logger import get_logger
14
 
15
  logger = get_logger(__name__)
16
 
 
8
  from diffusers import StableDiffusionImg2ImgPipeline
9
  from PIL import Image
10
 
11
+ from sdgen.sd.models import GenerationMetadata, Img2ImgConfig
12
+ from sdgen.utils.common import validate_resolution
13
+ from sdgen.utils.logger import get_logger
14
 
15
  logger = get_logger(__name__)
16
 
src/sdgen/sd/models.py CHANGED
@@ -63,44 +63,43 @@ class Img2ImgConfig:
63
 
64
  @dataclass
65
  class GenerationMetadata:
66
- """Output metadata for a generated image.
67
 
68
- Attributes:
69
- mode: Generation mode ("txt2img", "img2img", "upscale", ...).
70
- prompt: Prompt text.
71
- negative_prompt: Negative prompt text.
72
- steps: Number of diffusion steps.
73
- guidance_scale: CFG scale.
74
- width: Output width.
75
- height: Output height.
76
- seed: Resolved random seed.
77
- strength: Img2Img strength; None for Txt2Img.
78
- elapsed_seconds: Wall-clock runtime.
79
- timestamp: UTC timestamp.
80
- id: Unique entry ID.
81
- thumbnail: Local thumbnail path.
82
- full_image: Local full-size image path.
83
  """
84
 
85
- mode: str
86
- prompt: str
87
- negative_prompt: str = ""
88
- steps: int = 30
89
- guidance_scale: float = 7.5
90
- width: int = 512
91
- height: int = 512
92
- seed: Optional[int] = None
93
- strength: Optional[float] = None
94
  elapsed_seconds: float = 0.0
95
  timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat())
96
  id: Optional[str] = None
97
  thumbnail: Optional[str] = None
98
  full_image: Optional[str] = None
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  def to_dict(self) -> Dict[str, Any]:
101
- """Return a dict representation excluding None values."""
102
- data = asdict(self)
103
- return {key: value for key, value in data.items() if value is not None}
104
 
105
 
106
  @dataclass
 
63
 
64
  @dataclass
65
  class GenerationMetadata:
66
+ """Generic metadata for any generation mode.
67
 
68
+ Fields are optional depending on mode:
69
+ - Txt2Img: prompt, negative, steps, guidance
70
+ - Img2Img: prompt, negative, strength, steps, guidance
71
+ - Upscale: scale, original size, final size
 
 
 
 
 
 
 
 
 
 
 
72
  """
73
 
74
+ mode: str # "txt2img", "img2img", "upscale"
75
+
76
+ # Shared
 
 
 
 
 
 
77
  elapsed_seconds: float = 0.0
78
  timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat())
79
  id: Optional[str] = None
80
  thumbnail: Optional[str] = None
81
  full_image: Optional[str] = None
82
 
83
+ # Txt2Img / Img2Img
84
+ prompt: Optional[str] = None
85
+ negative_prompt: Optional[str] = None
86
+ steps: Optional[int] = None
87
+ guidance_scale: Optional[float] = None
88
+ seed: Optional[int] = None
89
+
90
+ # Img2Img only
91
+ strength: Optional[float] = None
92
+
93
+ # Upscale only
94
+ scale: Optional[float] = None
95
+ original_width: Optional[int] = None
96
+ original_height: Optional[int] = None
97
+ width: Optional[int] = None
98
+ height: Optional[int] = None
99
+
100
  def to_dict(self) -> Dict[str, Any]:
101
+ """Drop None values for clean JSON."""
102
+ return {k: v for k, v in asdict(self).items() if v is not None}
 
103
 
104
 
105
  @dataclass
src/sdgen/sd/pipeline.py CHANGED
@@ -12,7 +12,7 @@ from diffusers import (
12
  StableDiffusionPipeline,
13
  )
14
 
15
- from src.sdgen.utils.logger import get_logger
16
 
17
  logger = get_logger(__name__)
18
 
 
12
  StableDiffusionPipeline,
13
  )
14
 
15
+ from sdgen.utils.logger import get_logger
16
 
17
  logger = get_logger(__name__)
18
 
src/sdgen/ui/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
  from __future__ import annotations
2
 
3
- from src.sdgen.ui.layout import build_ui
4
 
5
  __all__ = ["build_ui"]
 
1
  from __future__ import annotations
2
 
3
+ from sdgen.ui.layout import build_ui
4
 
5
  __all__ = ["build_ui"]
src/sdgen/ui/layout.py CHANGED
@@ -2,24 +2,24 @@
2
 
3
  from __future__ import annotations
4
 
5
- from typing import Any, Dict, Tuple
6
 
7
  import gradio as gr
8
 
9
- from src.sdgen.sd.generator import generate_image
10
- from src.sdgen.sd.img2img import generate_img2img
11
- from src.sdgen.sd.models import Img2ImgConfig, Txt2ImgConfig
12
- from src.sdgen.ui.tabs import (
13
  build_history_tab,
14
  build_img2img_tab,
15
  build_presets_tab,
16
  build_txt2img_tab,
17
  build_upscaler_tab,
18
  )
19
- from src.sdgen.upscaler.upscaler import Upscaler
20
- from src.sdgen.utils.common import pretty_json, to_pil
21
- from src.sdgen.utils.history import save_history_entry
22
- from src.sdgen.utils.logger import get_logger
23
 
24
  logger = get_logger(__name__)
25
 
@@ -142,21 +142,14 @@ def _upscale_handler(
142
  raise gr.Error("Scale must be numeric (2 or 4).") from exc
143
 
144
  upscaler = Upscaler(scale=scale_int, prefer="ncnn")
145
- out_image = upscaler.upscale(pil_image)
146
-
147
- meta: Dict[str, Any] = {
148
- "mode": "upscale",
149
- "scale": scale_int,
150
- "width": out_image.width,
151
- "height": out_image.height,
152
- }
153
 
154
  try:
155
  save_history_entry(meta, out_image)
156
  except Exception as exc: # noqa: BLE001
157
  logger.exception("Failed to save history entry: %s", exc)
158
 
159
- return out_image, pretty_json(meta)
160
 
161
 
162
  def make_img2img_handler(model_choice, pipes):
 
2
 
3
  from __future__ import annotations
4
 
5
+ from typing import Any, Tuple
6
 
7
  import gradio as gr
8
 
9
+ from sdgen.sd.generator import generate_image
10
+ from sdgen.sd.img2img import generate_img2img
11
+ from sdgen.sd.models import Img2ImgConfig, Txt2ImgConfig
12
+ from sdgen.ui.tabs import (
13
  build_history_tab,
14
  build_img2img_tab,
15
  build_presets_tab,
16
  build_txt2img_tab,
17
  build_upscaler_tab,
18
  )
19
+ from sdgen.upscaler.upscaler import Upscaler
20
+ from sdgen.utils.common import pretty_json, to_pil
21
+ from sdgen.utils.history import save_history_entry
22
+ from sdgen.utils.logger import get_logger
23
 
24
  logger = get_logger(__name__)
25
 
 
142
  raise gr.Error("Scale must be numeric (2 or 4).") from exc
143
 
144
  upscaler = Upscaler(scale=scale_int, prefer="ncnn")
145
+ out_image, meta = upscaler.upscale(pil_image)
 
 
 
 
 
 
 
146
 
147
  try:
148
  save_history_entry(meta, out_image)
149
  except Exception as exc: # noqa: BLE001
150
  logger.exception("Failed to save history entry: %s", exc)
151
 
152
+ return out_image, pretty_json(meta.to_dict())
153
 
154
 
155
  def make_img2img_handler(model_choice, pipes):
src/sdgen/ui/tabs/history_tab.py CHANGED
@@ -7,13 +7,13 @@ from typing import Any, Dict, List, Optional, Tuple
7
  import gradio as gr
8
  from PIL import Image
9
 
10
- from src.sdgen.utils.common import short_prompt
11
- from src.sdgen.utils.history import (
12
  delete_history_entry,
13
  list_history,
14
  load_entry,
15
  )
16
- from src.sdgen.utils.logger import get_logger
17
 
18
  logger = get_logger(__name__)
19
 
 
7
  import gradio as gr
8
  from PIL import Image
9
 
10
+ from sdgen.utils.common import short_prompt
11
+ from sdgen.utils.history import (
12
  delete_history_entry,
13
  list_history,
14
  load_entry,
15
  )
16
+ from sdgen.utils.logger import get_logger
17
 
18
  logger = get_logger(__name__)
19
 
src/sdgen/ui/tabs/img2img_tab.py CHANGED
@@ -37,7 +37,6 @@ def build_img2img_tab(handler: Callable[..., Tuple[Any, dict]]) -> Img2ImgContro
37
  input_image = gr.Image(
38
  label="Input Image",
39
  type="numpy",
40
- tool="editor",
41
  )
42
 
43
  prompt = gr.Textbox(
 
37
  input_image = gr.Image(
38
  label="Input Image",
39
  type="numpy",
 
40
  )
41
 
42
  prompt = gr.Textbox(
src/sdgen/ui/tabs/presets_tab.py CHANGED
@@ -6,9 +6,9 @@ from typing import Any, Tuple
6
 
7
  import gradio as gr
8
 
9
- from src.sdgen.presets.styles import get_preset, list_presets
10
- from src.sdgen.ui.tabs.img2img_tab import Img2ImgControls
11
- from src.sdgen.ui.tabs.txt2img_tab import Txt2ImgControls
12
 
13
 
14
  def apply_preset(preset_name: Any) -> Tuple[Any, ...]:
 
6
 
7
  import gradio as gr
8
 
9
+ from sdgen.presets.styles import get_preset, list_presets
10
+ from sdgen.ui.tabs.img2img_tab import Img2ImgControls
11
+ from sdgen.ui.tabs.txt2img_tab import Txt2ImgControls
12
 
13
 
14
  def apply_preset(preset_name: Any) -> Tuple[Any, ...]:
src/sdgen/upscaler/realesrgan.py CHANGED
@@ -12,7 +12,7 @@ from typing import Final
12
  from PIL import Image
13
  from realesrgan_ncnn_py import Realesrgan
14
 
15
- from src.sdgen.utils.logger import get_logger
16
 
17
  logger = get_logger(__name__)
18
 
 
12
  from PIL import Image
13
  from realesrgan_ncnn_py import Realesrgan
14
 
15
+ from sdgen.utils.logger import get_logger
16
 
17
  logger = get_logger(__name__)
18
 
src/sdgen/upscaler/upscaler.py CHANGED
@@ -10,12 +10,14 @@ Planned:
10
 
11
  from __future__ import annotations
12
 
 
13
  from typing import Optional
14
 
15
  from PIL import Image
16
 
17
- from src.sdgen.upscaler.realesrgan import NCNNUpscaler
18
- from src.sdgen.utils.logger import get_logger
 
19
 
20
  logger = get_logger(__name__)
21
 
@@ -78,7 +80,7 @@ class Upscaler:
78
  logger.warning("NCNN RealESRGAN init failed: %s", err)
79
  self.engine = None
80
 
81
- def upscale(self, image: Image.Image) -> Image.Image:
82
  """Upscale the given image.
83
 
84
  Args:
@@ -91,5 +93,23 @@ class Upscaler:
91
  RuntimeError: If the engine is not initialized.
92
  """
93
  if self.engine is None:
94
- raise RuntimeError("Upscaler is not initialized.")
95
- return self.engine.upscale(image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  from __future__ import annotations
12
 
13
+ import time
14
  from typing import Optional
15
 
16
  from PIL import Image
17
 
18
+ from sdgen.sd.models import GenerationMetadata
19
+ from sdgen.upscaler.realesrgan import NCNNUpscaler
20
+ from sdgen.utils.logger import get_logger
21
 
22
  logger = get_logger(__name__)
23
 
 
80
  logger.warning("NCNN RealESRGAN init failed: %s", err)
81
  self.engine = None
82
 
83
+ def upscale(self, image: Image.Image) -> tuple[Image.Image, GenerationMetadata]:
84
  """Upscale the given image.
85
 
86
  Args:
 
93
  RuntimeError: If the engine is not initialized.
94
  """
95
  if self.engine is None:
96
+ raise RuntimeError("Upscaler not initialized.")
97
+
98
+ orig_w, orig_h = image.width, image.height
99
+ start = time.perf_counter()
100
+
101
+ out = self.engine.upscale(image)
102
+
103
+ elapsed = time.perf_counter() - start
104
+
105
+ meta = GenerationMetadata(
106
+ mode="upscale",
107
+ scale=float(self.scale),
108
+ original_width=orig_w,
109
+ original_height=orig_h,
110
+ width=out.width,
111
+ height=out.height,
112
+ elapsed_seconds=elapsed,
113
+ )
114
+
115
+ return out, meta
src/sdgen/utils/history.py CHANGED
@@ -18,14 +18,14 @@ from typing import Any, Dict, List, Optional, Tuple
18
 
19
  from PIL import Image
20
 
21
- from src.sdgen.config import (
22
  HISTORY_ENTRIES_DIR,
23
  HISTORY_FULL_DIR,
24
  HISTORY_ROOT,
25
  HISTORY_THUMBS_DIR,
26
  )
27
- from src.sdgen.sd.models import GenerationMetadata, HistorySummary
28
- from src.sdgen.utils.logger import get_logger
29
 
30
  logger = get_logger(__name__)
31
 
 
18
 
19
  from PIL import Image
20
 
21
+ from sdgen.config import (
22
  HISTORY_ENTRIES_DIR,
23
  HISTORY_FULL_DIR,
24
  HISTORY_ROOT,
25
  HISTORY_THUMBS_DIR,
26
  )
27
+ from sdgen.sd.models import GenerationMetadata, HistorySummary
28
+ from sdgen.utils.logger import get_logger
29
 
30
  logger = get_logger(__name__)
31
 
src/sdgen/utils/logger.py CHANGED
@@ -10,7 +10,7 @@ import logging
10
  from logging import Handler, Logger
11
  from logging.handlers import RotatingFileHandler
12
 
13
- from src.sdgen.config import LOGS_ROOT
14
 
15
  # Ensure logs directory exists
16
  LOGS_ROOT.mkdir(parents=True, exist_ok=True)
 
10
  from logging import Handler, Logger
11
  from logging.handlers import RotatingFileHandler
12
 
13
+ from sdgen.config import LOGS_ROOT
14
 
15
  # Ensure logs directory exists
16
  LOGS_ROOT.mkdir(parents=True, exist_ok=True)