修复midjourney
BIN
apps/jfile/files/25_5uvvahf6kj_d9c433c5_1.png
Normal file
|
After Width: | Height: | Size: 905 KiB |
BIN
apps/jfile/files/split_2acec28387_1.png
Normal file
|
After Width: | Height: | Size: 761 KiB |
BIN
apps/jfile/files/split_2acec28387_2.png
Normal file
|
After Width: | Height: | Size: 732 KiB |
BIN
apps/jfile/files/split_2acec28387_3.png
Normal file
|
After Width: | Height: | Size: 766 KiB |
BIN
apps/jfile/files/split_2acec28387_4.png
Normal file
|
After Width: | Height: | Size: 696 KiB |
BIN
apps/jfile/files/split_c4191175e4_1.png
Normal file
|
After Width: | Height: | Size: 845 KiB |
BIN
apps/jfile/files/split_c4191175e4_2.png
Normal file
|
After Width: | Height: | Size: 753 KiB |
BIN
apps/jfile/files/split_c4191175e4_3.png
Normal file
|
After Width: | Height: | Size: 745 KiB |
BIN
apps/jfile/files/split_c4191175e4_4.png
Normal file
|
After Width: | Height: | Size: 914 KiB |
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 1.8 MiB |
@ -289,10 +289,17 @@ class MidjourneyService:
|
||||
return image_urls
|
||||
|
||||
async def split_image(self, image_url):
|
||||
"""将一张大图切割成四张子图,保存到本地并返回URLs"""
|
||||
"""将一张大图切割成四张子图,保存到本地并返回URLs
|
||||
|
||||
Args:
|
||||
image_url: 原始图像的URL
|
||||
|
||||
Returns:
|
||||
包含四个子图像URL的列表,如果处理失败则返回None
|
||||
"""
|
||||
try:
|
||||
# 下载图像
|
||||
response = requests.get(image_url, proxies=self.proxies if self.proxies else None)
|
||||
response = requests.get(image_url, proxies=self.proxies if self.proxies else None, timeout=30)
|
||||
if response.status_code != 200:
|
||||
logger.error(f"下载图像失败,状态码: {response.status_code}")
|
||||
return None
|
||||
@ -303,9 +310,18 @@ class MidjourneyService:
|
||||
img = Image.open(io.BytesIO(image_data))
|
||||
width, height = img.size
|
||||
|
||||
# 获取原始图片格式
|
||||
original_format = img.format
|
||||
if not original_format:
|
||||
# 如果无法获取格式,从URL中提取
|
||||
parsed_url = urlparse(image_url)
|
||||
original_format = os.path.splitext(parsed_url.path)[1][1:].upper()
|
||||
if not original_format:
|
||||
original_format = 'PNG' # 默认使用PNG
|
||||
|
||||
# 确认图像尺寸约为2048x2048
|
||||
if width < 1500 or height < 1500:
|
||||
logger.error(f"图像尺寸不符合预期: {width}x{height}")
|
||||
logger.error(f"图像尺寸不符合预期: {width}x{height}, 应该接近2048x2048")
|
||||
return None
|
||||
|
||||
# 计算每个象限的尺寸
|
||||
@ -313,29 +329,70 @@ class MidjourneyService:
|
||||
half_height = height // 2
|
||||
|
||||
# 分割图像为四个象限
|
||||
top_left = img.crop((0, 0, half_width, half_height))
|
||||
top_right = img.crop((half_width, 0, width, half_height))
|
||||
bottom_left = img.crop((0, half_height, half_width, height))
|
||||
bottom_right = img.crop((half_width, half_height, width, height))
|
||||
quadrants = [
|
||||
img.crop((0, 0, half_width, half_height)), # 左上
|
||||
img.crop((half_width, 0, width, half_height)), # 右上
|
||||
img.crop((0, half_height, half_width, height)), # 左下
|
||||
img.crop((half_width, half_height, width, height)) # 右下
|
||||
]
|
||||
|
||||
# 生成唯一的图片名称前缀
|
||||
image_id = uuid.uuid4().hex[:10]
|
||||
|
||||
# 确保保存目录存在(使用绝对路径)
|
||||
save_dir = os.path.abspath(settings.save_dir)
|
||||
os.makedirs(save_dir, exist_ok=True)
|
||||
|
||||
# 保存图片到本地并生成URLs
|
||||
image_urls = []
|
||||
for i, quadrant in enumerate([top_left, top_right, bottom_left, bottom_right], 1):
|
||||
# 生成文件名和保存路径
|
||||
filename = f"split_{image_id}_{i}.png"
|
||||
file_path = os.path.join(settings.save_dir, filename)
|
||||
for i, quadrant in enumerate(quadrants, 1):
|
||||
try:
|
||||
# 使用原始格式保存
|
||||
filename = f"split_{image_id}_{i}.{original_format.lower()}"
|
||||
file_path = os.path.join(save_dir, filename)
|
||||
|
||||
# 保存图片,保持原始格式
|
||||
save_params = {"format": original_format}
|
||||
if original_format in ['PNG', 'JPEG', 'JPG']:
|
||||
save_params["optimize"] = True
|
||||
if original_format in ['JPEG', 'JPG']:
|
||||
save_params["quality"] = 95
|
||||
|
||||
quadrant.save(file_path, **save_params)
|
||||
|
||||
# 验证文件是否成功保存
|
||||
if not os.path.exists(file_path):
|
||||
raise Exception(f"文件保存失败: {file_path}")
|
||||
|
||||
# 验证文件大小
|
||||
file_size = os.path.getsize(file_path)
|
||||
if file_size == 0:
|
||||
raise Exception(f"保存的文件大小为0: {file_path}")
|
||||
|
||||
# 构建图片URL
|
||||
image_url = f"{settings.download_url}/{filename}"
|
||||
image_urls.append(image_url)
|
||||
|
||||
logger.info(f"成功保存分割图片 {i}/4: {filename} (大小: {file_size} 字节, 格式: {original_format})")
|
||||
except Exception as e:
|
||||
logger.error(f"保存分割图片 {i}/4 失败: {str(e)}")
|
||||
# 如果保存失败,删除已保存的图片
|
||||
for url in image_urls:
|
||||
try:
|
||||
file_path = os.path.join(save_dir, os.path.basename(url))
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
except Exception as del_e:
|
||||
logger.error(f"删除失败的图片文件时出错: {str(del_e)}")
|
||||
return None
|
||||
|
||||
# 保存图片
|
||||
quadrant.save(file_path, format="PNG")
|
||||
|
||||
# 构建图片URL
|
||||
image_url = f"{settings.download_url}/{filename}"
|
||||
image_urls.append(image_url)
|
||||
if len(image_urls) != 4:
|
||||
logger.error(f"分割图片数量不正确: 期望4张,实际{len(image_urls)}张")
|
||||
return None
|
||||
|
||||
logger.info(f"成功完成图片分割,生成4张子图")
|
||||
return image_urls
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"分割图像失败: {str(e)}")
|
||||
traceback.print_exc()
|
||||
|
||||
@ -17,9 +17,9 @@ class Settings(BaseSettings):
|
||||
upload_url: str = "http://images.jingrow.com:8080/api/v1/image"
|
||||
|
||||
# 图片保存配置
|
||||
save_dir: str = "../jfile/midjourney"
|
||||
save_dir: str = "../jfile/files"
|
||||
# Japi 静态资源下载URL
|
||||
download_url: str = "http://api.jingrow.com:9080/midjourney"
|
||||
download_url: str = "http://api.jingrow.com:9080/files"
|
||||
|
||||
# Jingrow Jcloud API 配置
|
||||
jingrow_api_url: str = "https://cloud.jingrow.com"
|
||||
|
||||
@ -175,87 +175,6 @@ def is_valid_image_url(url: str) -> bool:
|
||||
except:
|
||||
return False
|
||||
|
||||
def validate_image_file(file_path: str) -> bool:
|
||||
"""验证图片文件是否有效
|
||||
|
||||
Args:
|
||||
file_path: 图片文件路径
|
||||
|
||||
Returns:
|
||||
bool: 文件是否有效
|
||||
"""
|
||||
try:
|
||||
with Image.open(file_path) as img:
|
||||
img.verify()
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def get_image_size(image_url: str) -> Optional[Tuple[int, int]]:
|
||||
"""获取图片尺寸
|
||||
|
||||
Args:
|
||||
image_url: 图片URL
|
||||
|
||||
Returns:
|
||||
Optional[Tuple[int, int]]: 图片尺寸(宽,高),如果获取失败则返回None
|
||||
"""
|
||||
try:
|
||||
response = requests.get(image_url, verify=False, timeout=10)
|
||||
if response.status_code != 200:
|
||||
return None
|
||||
|
||||
with Image.open(io.BytesIO(response.content)) as img:
|
||||
return img.size
|
||||
except:
|
||||
return None
|
||||
|
||||
def is_valid_image_size(image_url: str, min_size: int = 512) -> bool:
|
||||
"""验证图片尺寸是否满足最小要求
|
||||
|
||||
Args:
|
||||
image_url: 图片URL
|
||||
min_size: 最小尺寸要求
|
||||
|
||||
Returns:
|
||||
bool: 图片尺寸是否满足要求
|
||||
"""
|
||||
size = get_image_size(image_url)
|
||||
if not size:
|
||||
return False
|
||||
width, height = size
|
||||
return width >= min_size and height >= min_size
|
||||
|
||||
def extract_image_urls_from_text(text: str) -> List[str]:
|
||||
"""从文本中提取图片URL
|
||||
|
||||
Args:
|
||||
text: 包含图片URL的文本
|
||||
|
||||
Returns:
|
||||
List[str]: 提取到的图片URL列表
|
||||
"""
|
||||
# 匹配常见的图片URL模式
|
||||
url_pattern = r'https?://[^\s<>"]+?\.(?:jpg|jpeg|png|webp|gif)(?:\?[^\s<>"]*)?'
|
||||
urls = re.findall(url_pattern, text, re.IGNORECASE)
|
||||
return [url for url in urls if is_valid_image_url(url)]
|
||||
|
||||
def sanitize_filename(filename: str) -> str:
|
||||
"""清理文件名,移除非法字符
|
||||
|
||||
Args:
|
||||
filename: 原始文件名
|
||||
|
||||
Returns:
|
||||
str: 清理后的文件名
|
||||
"""
|
||||
# 移除非法字符
|
||||
filename = re.sub(r'[<>:"/\\|?*]', '', filename)
|
||||
# 限制长度
|
||||
if len(filename) > 255:
|
||||
name, ext = os.path.splitext(filename)
|
||||
filename = name[:255-len(ext)] + ext
|
||||
return filename
|
||||
|
||||
def get_new_image_url(image_url: str) -> str:
|
||||
"""将图片URL转换为新的存储URL
|
||||
|
||||