返回 Pixelle-Video
image_dashscope.py
根目录 / pixelle_video / services / api_services / image_dashscope.py
1 import os
2 import json
3 import logging
4 import time
5 import uuid
6 import threading
7 from contextlib import contextmanager
8 from typing import Any
9 try:
10 import dashscope
11 from dashscope import MultiModalConversation
12 from dashscope.aigc.image_generation import ImageGeneration
13 except ImportError:
14 dashscope = None
15 MultiModalConversation = None
16 ImageGeneration = None
17 try:
18 from .image_processor import ImageProcessor
19 except ImportError:
20 from image_processor import ImageProcessor
21
22 class DashScopeClient:
23 _proxy_env_lock = threading.Lock()
24
25 def __init__(self, api_key=None, base_url=None, local_proxy=None):
26 self.api_key = api_key or os.getenv("DASHSCOPE_API_KEY")
27 # 默认使用中国(北京)地域 API,如果环境变量或参数未设置则使用默认地址
28 self.base_url = base_url or os.getenv("DASHSCOPE_BASE_URL")
29 self.local_proxy = local_proxy
30 if dashscope:
31 dashscope.api_key = self.api_key
32 dashscope.base_http_api_url = self.base_url
33 self.image_processor = ImageProcessor(local_proxy=local_proxy)
34
35 @contextmanager
36 def _proxy_env(self):
37 if not self.local_proxy:
38 yield
39 return
40
41 with self._proxy_env_lock:
42 keys = ("HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy")
43 old_values = {key: os.environ.get(key) for key in keys}
44 try:
45 for key in keys:
46 os.environ[key] = self.local_proxy
47 yield
48 finally:
49 for key, value in old_values.items():
50 if value is None:
51 os.environ.pop(key, None)
52 else:
53 os.environ[key] = value
54
55 def _extract_image_urls(self, payload: Any) -> list[str]:
56 """Extract image URLs from the different DashScope response shapes."""
57 results = []
58
59 def to_plain(value):
60 if value is None:
61 return None
62 if isinstance(value, (str, int, float, bool)):
63 return value
64 if isinstance(value, list):
65 return [to_plain(item) for item in value]
66 if isinstance(value, tuple):
67 return [to_plain(item) for item in value]
68 if isinstance(value, dict):
69 return {key: to_plain(val) for key, val in value.items()}
70 if hasattr(value, "to_dict"):
71 return to_plain(value.to_dict())
72 if hasattr(value, "__dict__"):
73 return {
74 key: to_plain(val)
75 for key, val in value.__dict__.items()
76 if not key.startswith("_")
77 }
78 return value
79
80 def walk(value):
81 if isinstance(value, dict):
82 for key, val in value.items():
83 if key in {"image", "url", "image_url"} and isinstance(val, str):
84 results.append(val)
85 else:
86 walk(val)
87 elif isinstance(value, list):
88 for item in value:
89 walk(item)
90
91 walk(to_plain(payload))
92 return [url for url in results if isinstance(url, str)]
93
94 def generate_image(self, prompt, model="wan2.7-image", size="1024*1024", n=1, session_id=None, save_dir=None):
95 """
96 Text to Image generation using DashScope
97 """
98 if ImageGeneration is None:
99 raise RuntimeError("dashscope package not installed. Run: pip install dashscope")
100
101 try:
102 messages = [{"role": "user", "content": [{"text": prompt}]}]
103 with self._proxy_env():
104 response = ImageGeneration.call(
105 model=model,
106 api_key=self.api_key,
107 messages=messages,
108 n=n,
109 size=size,
110 watermark=False,
111 )
112
113 if response.status_code == 200:
114 results = self._extract_image_urls(getattr(response, "output", None))
115 if not results:
116 raise RuntimeError(f"DashScope image generation returned no image URLs. output={getattr(response, 'output', None)}")
117
118 # Check if we should download
119 if save_dir:
120 os.makedirs(save_dir, exist_ok=True)
121 local_files = []
122 for i, url in enumerate(results):
123 file_name = f"ds_{session_id if session_id else 'nosess'}_{int(time.time())}_{i}_{uuid.uuid4().hex[:6]}.png"
124 file_path = os.path.join(save_dir, file_name)
125 if self.image_processor.download_image(url, file_path):
126 local_files.append(file_path)
127 return local_files
128
129 return results
130 else:
131 raise RuntimeError(f"Image generation failed: {response.code}, {response.message}, status={response.status_code}")
132 except Exception as e:
133 logging.error(f"Error in generate_image (DashScope): {e}")
134 raise
135
136 def edit_image(self, prompt, image_urls, model="wan2.7-image", size="1920*1080", n=1, session_id=None, save_dir=None):
137 """
138 Image editing/compositing using DashScope ImageGeneration
139 """
140 if ImageGeneration is None:
141 raise RuntimeError("dashscope package not installed. Run: pip install dashscope")
142
143 # Prepare content
144 content_list = []
145 for img_url in image_urls:
146 content_list.append({"image": img_url})
147 content_list.append({"text": prompt})
148
149 messages = [
150 {
151 "role": "user",
152 "content": content_list
153 }
154 ]
155
156 try:
157 # Use ImageGeneration.call with messages, same as generate_image
158 with self._proxy_env():
159 response = ImageGeneration.call(
160 model=model,
161 api_key=self.api_key,
162 messages=messages,
163 n=n,
164 size=size,
165 watermark=False,
166 )
167
168 if response.status_code == 200:
169 results = self._extract_image_urls(getattr(response, "output", None))
170 if not results:
171 raise RuntimeError(f"DashScope image edit returned no image URLs. output={getattr(response, 'output', None)}")
172
173 # Check if we should download
174 if save_dir:
175 os.makedirs(save_dir, exist_ok=True)
176 local_files = []
177 for i, url in enumerate(results):
178 file_name = f"ds_{session_id if session_id else 'nosess'}_{int(time.time())}_{i}_{uuid.uuid4().hex[:6]}.png"
179 file_path = os.path.join(save_dir, file_name)
180 if self.image_processor.download_image(url, file_path):
181 local_files.append(file_path)
182 return local_files
183
184 return results
185 else:
186 raise RuntimeError(f"Image edit failed: {response.code}, {response.message}, status={response.status_code}")
187 except Exception as e:
188 logging.error(f"Error in edit_image: {e}")
189 raise
190
191
192 if __name__ == "__main__":
193 import sys
194 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
195 from config import Config
196
197 print("=== DashScope 图片生成可用性测试 ===")
198 MODELS=["wan2.6-t2i", "wan2.7-image", "wan2.7-image-pro"]
199 save_dir = "code/result/image/test_avail"
200 api_key = Config.DASHSCOPE_API_KEY
201 base_url = Config.DASHSCOPE_BASE_URL
202 if not api_key:
203 print("✗ DASHSCOPE_API_KEY 未设置,跳过")
204 sys.exit(1)
205 print(f" API Key: {api_key[:6]}***{api_key[-4:]}")
206 print(f" Base URL: {base_url}")
207 client = DashScopeClient(api_key=api_key, base_url=base_url)
208
209 # 文生图
210 print("\n=== 文生图测试 ===")
211 prompt = "一只橘猫躺在阳光下的窗台上,水彩画风格"
212 for model in MODELS:
213 print(f"\nPrompt: {prompt}")
214 print(f"model: {model}")
215 os.makedirs(save_dir, exist_ok=True)
216 t0 = time.time()
217 try:
218 paths = client.generate_image(
219 prompt=prompt, model=model,
220 size="1024*1024", save_dir=save_dir,
221 )
222 elapsed = time.time() - t0
223 if paths:
224 print(f"✓ 生成 {len(paths)} 张图片 ({elapsed:.1f}s): {paths}")
225 else:
226 print(f"✗ 返回空列表 ({elapsed:.1f}s)")
227 except Exception as e:
228 print(f"✗ 失败: {e}")
229 sys.exit(1)
230
231 # 图生图
232 print("\n=== 图生图测试 ===")
233 img_path = "code/result/image/test_avail/test_input.png"
234 prompt = "在这张图片的基础上,添加一些飞舞的樱花花瓣,绘制为水彩画风格"
235 for model in MODELS:
236 print(f"\nPrompt: {prompt}")
237 print(f"model: {model}")
238 os.makedirs(save_dir, exist_ok=True)
239 t0 = time.time()
240 try:
241 paths = client.edit_image(
242 prompt=prompt, image_urls=[img_path], model=model,
243 size="1024*1024", save_dir=save_dir,
244 )
245 elapsed = time.time() - t0
246 if paths:
247 print(f"✓ 生成 {len(paths)} 张图片 ({elapsed:.1f}s): {paths}")
248 else:
249 print(f"✗ 返回空列表 ({elapsed:.1f}s)")
250 except Exception as e:
251 print(f"✗ 失败: {e}")
252 sys.exit(1)
253
253 lines PYTHON