Latest web ui that support DefectFill and DefectDiffu model run simultaneusly
Browse files
ArtiAgent - DefectDiffu/src/artiagent_orchestrator.py
CHANGED
|
@@ -271,7 +271,7 @@ class ArtiAgentOrchestrator:
|
|
| 271 |
# ------------------------------------------------------------------
|
| 272 |
|
| 273 |
def plan(self, product_description: str, image: np.ndarray,
|
| 274 |
-
defect_type: Optional[str] = None,
|
| 275 |
"""Agent plans defects based on product knowledge."""
|
| 276 |
print(f"\\n{'='*60}")
|
| 277 |
print("[Agent] Step 1: Planning defects from product description...")
|
|
@@ -285,7 +285,7 @@ class ArtiAgentOrchestrator:
|
|
| 285 |
image,
|
| 286 |
money_manager=self.money_manager,
|
| 287 |
target_defect_type=defect_type,
|
| 288 |
-
|
| 289 |
)
|
| 290 |
|
| 291 |
if plan is None:
|
|
@@ -543,7 +543,7 @@ class ArtiAgentOrchestrator:
|
|
| 543 |
# ------------------------------------------------------------------
|
| 544 |
|
| 545 |
def run(self, product_description: str, image_path: str,
|
| 546 |
-
caption: Optional[str] = None,
|
| 547 |
defect_type: Optional[str] = None) -> Dict:
|
| 548 |
"""Run the full agentic pipeline with DefectDiffu."""
|
| 549 |
start_time = datetime.now()
|
|
@@ -558,10 +558,10 @@ class ArtiAgentOrchestrator:
|
|
| 558 |
|
| 559 |
print(f"[Agent] Loaded image: {image.shape}")
|
| 560 |
|
| 561 |
-
plan = self.plan(product_description, image, defect_type=defect_type,
|
| 562 |
|
| 563 |
results = []
|
| 564 |
-
defects_to_process = plan.possible_defects[:
|
| 565 |
print(f"[Agent] Processing top {len(defects_to_process)} of {len(plan.possible_defects)} planned defects")
|
| 566 |
|
| 567 |
for i, defect_plan in enumerate(defects_to_process):
|
|
@@ -741,8 +741,8 @@ def main():
|
|
| 741 |
help='Specific defect type to generate (e.g., bubble, scratch)')
|
| 742 |
parser.add_argument('--output-dir', default='./defect_output', help='Output directory')
|
| 743 |
parser.add_argument('--caption', default=None, help='Optional image caption')
|
| 744 |
-
parser.add_argument('--
|
| 745 |
-
help='
|
| 746 |
parser.add_argument('--device', default='cuda', help='Device (cuda/cpu)')
|
| 747 |
parser.add_argument('--vlm-model', default='gemma3:12b', help='Local VLM model')
|
| 748 |
parser.add_argument('--image-size', type=int, default=512,
|
|
@@ -766,7 +766,7 @@ def main():
|
|
| 766 |
product_description=args.product_desc,
|
| 767 |
image_path=args.image,
|
| 768 |
caption=args.caption,
|
| 769 |
-
|
| 770 |
defect_type=args.defect_type
|
| 771 |
)
|
| 772 |
|
|
|
|
| 271 |
# ------------------------------------------------------------------
|
| 272 |
|
| 273 |
def plan(self, product_description: str, image: np.ndarray,
|
| 274 |
+
defect_type: Optional[str] = None, num_defects: int = 3):
|
| 275 |
"""Agent plans defects based on product knowledge."""
|
| 276 |
print(f"\\n{'='*60}")
|
| 277 |
print("[Agent] Step 1: Planning defects from product description...")
|
|
|
|
| 285 |
image,
|
| 286 |
money_manager=self.money_manager,
|
| 287 |
target_defect_type=defect_type,
|
| 288 |
+
num_defects=num_defects
|
| 289 |
)
|
| 290 |
|
| 291 |
if plan is None:
|
|
|
|
| 543 |
# ------------------------------------------------------------------
|
| 544 |
|
| 545 |
def run(self, product_description: str, image_path: str,
|
| 546 |
+
caption: Optional[str] = None, num_defects: int = 3,
|
| 547 |
defect_type: Optional[str] = None) -> Dict:
|
| 548 |
"""Run the full agentic pipeline with DefectDiffu."""
|
| 549 |
start_time = datetime.now()
|
|
|
|
| 558 |
|
| 559 |
print(f"[Agent] Loaded image: {image.shape}")
|
| 560 |
|
| 561 |
+
plan = self.plan(product_description, image, defect_type=defect_type, num_defects=num_defects)
|
| 562 |
|
| 563 |
results = []
|
| 564 |
+
defects_to_process = plan.possible_defects[:num_defects]
|
| 565 |
print(f"[Agent] Processing top {len(defects_to_process)} of {len(plan.possible_defects)} planned defects")
|
| 566 |
|
| 567 |
for i, defect_plan in enumerate(defects_to_process):
|
|
|
|
| 741 |
help='Specific defect type to generate (e.g., bubble, scratch)')
|
| 742 |
parser.add_argument('--output-dir', default='./defect_output', help='Output directory')
|
| 743 |
parser.add_argument('--caption', default=None, help='Optional image caption')
|
| 744 |
+
parser.add_argument('--num-defects', type=int, default=3,
|
| 745 |
+
help='Number of defects to generate (default: 3)')
|
| 746 |
parser.add_argument('--device', default='cuda', help='Device (cuda/cpu)')
|
| 747 |
parser.add_argument('--vlm-model', default='gemma3:12b', help='Local VLM model')
|
| 748 |
parser.add_argument('--image-size', type=int, default=512,
|
|
|
|
| 766 |
product_description=args.product_desc,
|
| 767 |
image_path=args.image,
|
| 768 |
caption=args.caption,
|
| 769 |
+
num_defects=args.num_defects,
|
| 770 |
defect_type=args.defect_type
|
| 771 |
)
|
| 772 |
|
ArtiAgent - DefectDiffu/src/pipeline/gsam_detector.py
CHANGED
|
@@ -110,7 +110,7 @@ class GSAMDetector:
|
|
| 110 |
device: Device to use (cuda/cpu)
|
| 111 |
openai_client: OpenAI client for vocabulary generation
|
| 112 |
"""
|
| 113 |
-
self.gsam_path = os.
|
| 114 |
|
| 115 |
# Set default paths if not provided
|
| 116 |
if grounding_config_file is None:
|
|
|
|
| 110 |
device: Device to use (cuda/cpu)
|
| 111 |
openai_client: OpenAI client for vocabulary generation
|
| 112 |
"""
|
| 113 |
+
self.gsam_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 114 |
|
| 115 |
# Set default paths if not provided
|
| 116 |
if grounding_config_file is None:
|
ArtiAgent - DefectDiffu/src/pipeline/prompts.py
CHANGED
|
@@ -134,12 +134,13 @@ def plan_defects_for_product(
|
|
| 134 |
image,
|
| 135 |
money_manager=None,
|
| 136 |
target_defect_type: Optional[str] = None,
|
| 137 |
-
|
| 138 |
):
|
| 139 |
"""
|
| 140 |
Agent reasons about possible defects and outputs DefectDiffu-native plans.
|
| 141 |
Each plan includes the three text prompts (c_p, c_d, c_f) and w_d/w_p scales.
|
| 142 |
"""
|
|
|
|
| 143 |
if client is None:
|
| 144 |
client = default_client
|
| 145 |
base64_image = encode_image_to_base64(image)
|
|
@@ -159,7 +160,7 @@ You are given:
|
|
| 159 |
2. A clean reference image of the product
|
| 160 |
{defect_instruction}
|
| 161 |
|
| 162 |
-
Your task is to analyze the product and propose
|
| 163 |
|
| 164 |
For EACH defect, you must produce THREE text prompts for the DefectDiffu diffusion model:
|
| 165 |
- c_p (background prompt): "A photo of <product name>"
|
|
|
|
| 134 |
image,
|
| 135 |
money_manager=None,
|
| 136 |
target_defect_type: Optional[str] = None,
|
| 137 |
+
num_defects: int = 3
|
| 138 |
):
|
| 139 |
"""
|
| 140 |
Agent reasons about possible defects and outputs DefectDiffu-native plans.
|
| 141 |
Each plan includes the three text prompts (c_p, c_d, c_f) and w_d/w_p scales.
|
| 142 |
"""
|
| 143 |
+
|
| 144 |
if client is None:
|
| 145 |
client = default_client
|
| 146 |
base64_image = encode_image_to_base64(image)
|
|
|
|
| 160 |
2. A clean reference image of the product
|
| 161 |
{defect_instruction}
|
| 162 |
|
| 163 |
+
Your task is to analyze the product and propose {num_defects} realistic manufacturing or handling defects.
|
| 164 |
|
| 165 |
For EACH defect, you must produce THREE text prompts for the DefectDiffu diffusion model:
|
| 166 |
- c_p (background prompt): "A photo of <product name>"
|
ArtiAgent - DefectFill/src/pipeline/gsam_detector.py
CHANGED
|
@@ -176,7 +176,7 @@ class GSAMDetector:
|
|
| 176 |
entity_predictions = []
|
| 177 |
|
| 178 |
# Use VLM to get bboxes for each entity
|
| 179 |
-
from prompts import get_entity_bboxes
|
| 180 |
|
| 181 |
all_bboxes = {} # entity -> list of bboxes
|
| 182 |
|
|
|
|
| 176 |
entity_predictions = []
|
| 177 |
|
| 178 |
# Use VLM to get bboxes for each entity
|
| 179 |
+
from pipeline.prompts import get_entity_bboxes
|
| 180 |
|
| 181 |
all_bboxes = {} # entity -> list of bboxes
|
| 182 |
|
app.py
CHANGED
|
@@ -25,6 +25,7 @@ import numpy as np
|
|
| 25 |
import uuid
|
| 26 |
import io
|
| 27 |
import re
|
|
|
|
| 28 |
|
| 29 |
|
| 30 |
# =============================================================================
|
|
@@ -78,36 +79,124 @@ if not DEFECTDIFFU_CONFIG_PATH.exists():
|
|
| 78 |
json.dump({"ckpt_path": "", "vae_path": "", "vlm_model": "gemma3:12b"}, f, indent=2)
|
| 79 |
|
| 80 |
# Setup sys.path for imports
|
| 81 |
-
sys.path.insert(0, str(DEFECTFILL_ROOT / 'src' / 'segment_anything'))
|
| 82 |
-
sys.path.insert(0, str(DEFECTFILL_ROOT / 'src'))
|
| 83 |
-
sys.path.insert(0, str(DEFECTFILL_ROOT / 'pipeline'))
|
| 84 |
-
sys.path.insert(0, str(DEFECTFILL_ROOT))
|
| 85 |
-
sys.path.insert(0, str(DEFECTDIFFU_ROOT))
|
| 86 |
-
sys.path.insert(0, str(DEFECTDIFFU_ROOT / '
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
from flask import Flask, render_template, request, jsonify, send_from_directory
|
| 107 |
from flask_cors import CORS
|
| 108 |
|
| 109 |
app = Flask(__name__, template_folder=str(BASE_DIR / "templates"), static_folder=str(BASE_DIR / "static"))
|
| 110 |
-
app.config["MAX_CONTENT_LENGTH"] =
|
| 111 |
CORS(app)
|
| 112 |
|
| 113 |
# =============================================================================
|
|
@@ -263,7 +352,7 @@ def run_generation_job(job_id, params, mode='defectfill'):
|
|
| 263 |
result = orchestrator.run(
|
| 264 |
product_description=params['product_desc'],
|
| 265 |
image_path=clean_path,
|
| 266 |
-
|
| 267 |
defect_type=params['defect_type']
|
| 268 |
)
|
| 269 |
|
|
|
|
| 25 |
import uuid
|
| 26 |
import io
|
| 27 |
import re
|
| 28 |
+
import importlib.util
|
| 29 |
|
| 30 |
|
| 31 |
# =============================================================================
|
|
|
|
| 79 |
json.dump({"ckpt_path": "", "vae_path": "", "vlm_model": "gemma3:12b"}, f, indent=2)
|
| 80 |
|
| 81 |
# Setup sys.path for imports
|
| 82 |
+
# sys.path.insert(0, str(DEFECTFILL_ROOT / 'src' / 'segment_anything'))
|
| 83 |
+
# sys.path.insert(0, str(DEFECTFILL_ROOT / 'src'))
|
| 84 |
+
# sys.path.insert(0, str(DEFECTFILL_ROOT / 'pipeline'))
|
| 85 |
+
# sys.path.insert(0, str(DEFECTFILL_ROOT))
|
| 86 |
+
# sys.path.insert(0, str(DEFECTDIFFU_ROOT))
|
| 87 |
+
# sys.path.insert(0, str(DEFECTDIFFU_ROOT / 'src'))
|
| 88 |
+
# sys.path.insert(0, str(DEFECTDIFFU_ROOT / 'pipeline'))
|
| 89 |
+
|
| 90 |
+
# =============================================================================
|
| 91 |
+
# Dynamic Import Helpers (avoid module name collisions)
|
| 92 |
+
# =============================================================================
|
| 93 |
+
|
| 94 |
+
def load_orchestrator_from_path(module_path, class_name="ArtiAgentOrchestrator", extra_paths=None):
|
| 95 |
+
"""
|
| 96 |
+
Dynamically import a class from a Python file, temporarily adding
|
| 97 |
+
extra_paths to sys.path so that the module's internal imports
|
| 98 |
+
resolve to the correct project.
|
| 99 |
+
"""
|
| 100 |
+
if not module_path.exists():
|
| 101 |
+
print(f"[ERROR] Module file not found: {module_path}")
|
| 102 |
+
return None
|
| 103 |
+
|
| 104 |
+
original_sys_path = sys.path[:] # save current state
|
| 105 |
+
|
| 106 |
+
try:
|
| 107 |
+
# Insert project‑specific paths at the front
|
| 108 |
+
if extra_paths:
|
| 109 |
+
for p in reversed(extra_paths): # reverse to keep the given order
|
| 110 |
+
p_str = str(p)
|
| 111 |
+
if p_str not in sys.path:
|
| 112 |
+
sys.path.insert(0, p_str)
|
| 113 |
+
|
| 114 |
+
# Also ensure the module's own directory is available
|
| 115 |
+
module_dir = module_path.parent
|
| 116 |
+
if module_dir not in sys.path:
|
| 117 |
+
sys.path.insert(0, str(module_dir))
|
| 118 |
+
|
| 119 |
+
# Use a unique module name to avoid caching collisions
|
| 120 |
+
module_id = f"_dynamic_{class_name}_{uuid.uuid4().hex[:8]}"
|
| 121 |
+
spec = importlib.util.spec_from_file_location(module_id, str(module_path))
|
| 122 |
+
module = importlib.util.module_from_spec(spec)
|
| 123 |
+
spec.loader.exec_module(module)
|
| 124 |
+
|
| 125 |
+
cls = getattr(module, class_name, None)
|
| 126 |
+
if cls is None:
|
| 127 |
+
print(f"[ERROR] Class '{class_name}' not found in {module_path}")
|
| 128 |
+
return cls
|
| 129 |
+
|
| 130 |
+
except Exception as e:
|
| 131 |
+
print(f"[ERROR] Failed to load {module_path}: {e}")
|
| 132 |
+
traceback.print_exc()
|
| 133 |
+
return None
|
| 134 |
+
|
| 135 |
+
finally:
|
| 136 |
+
# Restore original sys.path to avoid leaking
|
| 137 |
+
sys.path[:] = original_sys_path
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
# DefectFill imports — load directly from file to avoid sys.path collision with DefectFill
|
| 141 |
+
extra_paths_df = [
|
| 142 |
+
DEFECTFILL_ROOT / 'src' / 'segment_anything',
|
| 143 |
+
DEFECTFILL_ROOT / 'src',
|
| 144 |
+
DEFECTFILL_ROOT / 'pipeline',
|
| 145 |
+
DEFECTFILL_ROOT,
|
| 146 |
+
]
|
| 147 |
+
|
| 148 |
+
df_orchestrator_path = DEFECTFILL_ROOT / "src" / "artiagent_orchestrator.py"
|
| 149 |
+
DefectFillOrchestrator = load_orchestrator_from_path(
|
| 150 |
+
df_orchestrator_path,
|
| 151 |
+
"ArtiAgentOrchestrator",
|
| 152 |
+
extra_paths_df
|
| 153 |
+
)
|
| 154 |
+
print("[INFO] DefectFill orchestrator is ready.", flush=True)
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
# DefectDiffu imports — load directly from file to avoid name collision with DefectDiffu
|
| 158 |
+
extra_paths_dd = [
|
| 159 |
+
DEFECTDIFFU_ROOT / 'src',
|
| 160 |
+
DEFECTDIFFU_ROOT / "engine" / "DefectDiffu",
|
| 161 |
+
DEFECTDIFFU_ROOT,
|
| 162 |
+
]
|
| 163 |
+
|
| 164 |
+
dd_orchestrator_path = DEFECTDIFFU_ROOT / 'src' / "artiagent_orchestrator.py"
|
| 165 |
+
DefectDiffuOrchestrator = load_orchestrator_from_path(
|
| 166 |
+
dd_orchestrator_path,
|
| 167 |
+
"ArtiAgentOrchestrator",
|
| 168 |
+
extra_paths_dd
|
| 169 |
+
)
|
| 170 |
+
print("[INFO] DefectDiffu orchestrator is ready.")
|
| 171 |
+
|
| 172 |
+
def load_helper_from_path(module_path, function_name):
|
| 173 |
+
# Use a generic loader (can reuse load_orchestrator_from_path with no class)
|
| 174 |
+
# But for functions, you can load the module and extract the function.
|
| 175 |
+
original_sys_path = sys.path[:]
|
| 176 |
+
try:
|
| 177 |
+
# Add DefectFill paths (or pass as extra_paths)
|
| 178 |
+
sys.path.insert(0, str(DEFECTFILL_ROOT / 'src'))
|
| 179 |
+
sys.path.insert(0, str(DEFECTFILL_ROOT))
|
| 180 |
+
spec = importlib.util.spec_from_file_location(f"_helper_{uuid.uuid4().hex[:8]}", str(module_path))
|
| 181 |
+
module = importlib.util.module_from_spec(spec)
|
| 182 |
+
spec.loader.exec_module(module)
|
| 183 |
+
return getattr(module, function_name, None)
|
| 184 |
+
finally:
|
| 185 |
+
sys.path[:] = original_sys_path
|
| 186 |
+
|
| 187 |
+
# Usage:
|
| 188 |
+
align_image_to_reference = load_helper_from_path(
|
| 189 |
+
DEFECTFILL_ROOT / 'src' / 'align_image_to_reference.py', 'align_image_to_reference'
|
| 190 |
+
)
|
| 191 |
+
check_product_training_status = load_helper_from_path(
|
| 192 |
+
DEFECTFILL_ROOT / 'src' / 'detect_similar_product.py', 'check_product_training_status'
|
| 193 |
+
)
|
| 194 |
|
| 195 |
from flask import Flask, render_template, request, jsonify, send_from_directory
|
| 196 |
from flask_cors import CORS
|
| 197 |
|
| 198 |
app = Flask(__name__, template_folder=str(BASE_DIR / "templates"), static_folder=str(BASE_DIR / "static"))
|
| 199 |
+
app.config["MAX_CONTENT_LENGTH"] = 100 * 1024 * 1024 # 100MB upload limit
|
| 200 |
CORS(app)
|
| 201 |
|
| 202 |
# =============================================================================
|
|
|
|
| 352 |
result = orchestrator.run(
|
| 353 |
product_description=params['product_desc'],
|
| 354 |
image_path=clean_path,
|
| 355 |
+
num_defects=params['num_defects'],
|
| 356 |
defect_type=params['defect_type']
|
| 357 |
)
|
| 358 |
|
config/defectdiffu_config.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
{
|
| 2 |
-
"ckpt_path": "C:\\Users\\admin_mtds\\OneDrive\\Desktop\\ChinKuan\\ArtiAgent -
|
| 3 |
-
"vae_path": "C:\\Users\\admin_mtds\\OneDrive\\Desktop\\ChinKuan\\ArtiAgent -
|
| 4 |
"vlm_model": "gemma3:12b"
|
| 5 |
}
|
|
|
|
| 1 |
{
|
| 2 |
+
"ckpt_path": "C:\\Users\\admin_mtds\\OneDrive\\Desktop\\ChinKuan\\ArtiAgent - latest WebUI\\ArtiAgent - DefectDiffu\\engine\\DefectDiffu\\checkpoint_old-4\\model_300.pth",
|
| 3 |
+
"vae_path": "C:\\Users\\admin_mtds\\OneDrive\\Desktop\\ChinKuan\\ArtiAgent - latest WebUI\\ArtiAgent - DefectDiffu\\engine\\DefectDiffu\\checkpoints\\sd-vae-ft-mse",
|
| 4 |
"vlm_model": "gemma3:12b"
|
| 5 |
}
|