修复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
|
return image_urls
|
||||||
|
|
||||||
async def split_image(self, image_url):
|
async def split_image(self, image_url):
|
||||||
"""将一张大图切割成四张子图,保存到本地并返回URLs"""
|
"""将一张大图切割成四张子图,保存到本地并返回URLs
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image_url: 原始图像的URL
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
包含四个子图像URL的列表,如果处理失败则返回None
|
||||||
|
"""
|
||||||
try:
|
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:
|
if response.status_code != 200:
|
||||||
logger.error(f"下载图像失败,状态码: {response.status_code}")
|
logger.error(f"下载图像失败,状态码: {response.status_code}")
|
||||||
return None
|
return None
|
||||||
@ -303,9 +310,18 @@ class MidjourneyService:
|
|||||||
img = Image.open(io.BytesIO(image_data))
|
img = Image.open(io.BytesIO(image_data))
|
||||||
width, height = img.size
|
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
|
# 确认图像尺寸约为2048x2048
|
||||||
if width < 1500 or height < 1500:
|
if width < 1500 or height < 1500:
|
||||||
logger.error(f"图像尺寸不符合预期: {width}x{height}")
|
logger.error(f"图像尺寸不符合预期: {width}x{height}, 应该接近2048x2048")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# 计算每个象限的尺寸
|
# 计算每个象限的尺寸
|
||||||
@ -313,29 +329,70 @@ class MidjourneyService:
|
|||||||
half_height = height // 2
|
half_height = height // 2
|
||||||
|
|
||||||
# 分割图像为四个象限
|
# 分割图像为四个象限
|
||||||
top_left = img.crop((0, 0, half_width, half_height))
|
quadrants = [
|
||||||
top_right = img.crop((half_width, 0, width, half_height))
|
img.crop((0, 0, half_width, half_height)), # 左上
|
||||||
bottom_left = img.crop((0, half_height, half_width, height))
|
img.crop((half_width, 0, width, half_height)), # 右上
|
||||||
bottom_right = img.crop((half_width, half_height, width, height))
|
img.crop((0, half_height, half_width, height)), # 左下
|
||||||
|
img.crop((half_width, half_height, width, height)) # 右下
|
||||||
|
]
|
||||||
|
|
||||||
# 生成唯一的图片名称前缀
|
# 生成唯一的图片名称前缀
|
||||||
image_id = uuid.uuid4().hex[:10]
|
image_id = uuid.uuid4().hex[:10]
|
||||||
|
|
||||||
|
# 确保保存目录存在(使用绝对路径)
|
||||||
|
save_dir = os.path.abspath(settings.save_dir)
|
||||||
|
os.makedirs(save_dir, exist_ok=True)
|
||||||
|
|
||||||
# 保存图片到本地并生成URLs
|
# 保存图片到本地并生成URLs
|
||||||
image_urls = []
|
image_urls = []
|
||||||
for i, quadrant in enumerate([top_left, top_right, bottom_left, bottom_right], 1):
|
for i, quadrant in enumerate(quadrants, 1):
|
||||||
# 生成文件名和保存路径
|
try:
|
||||||
filename = f"split_{image_id}_{i}.png"
|
# 使用原始格式保存
|
||||||
file_path = os.path.join(settings.save_dir, filename)
|
filename = f"split_{image_id}_{i}.{original_format.lower()}"
|
||||||
|
file_path = os.path.join(save_dir, filename)
|
||||||
|
|
||||||
# 保存图片
|
# 保存图片,保持原始格式
|
||||||
quadrant.save(file_path, format="PNG")
|
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
|
||||||
|
|
||||||
# 构建图片URL
|
quadrant.save(file_path, **save_params)
|
||||||
image_url = f"{settings.download_url}/{filename}"
|
|
||||||
image_urls.append(image_url)
|
|
||||||
|
|
||||||
|
# 验证文件是否成功保存
|
||||||
|
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
|
||||||
|
|
||||||
|
if len(image_urls) != 4:
|
||||||
|
logger.error(f"分割图片数量不正确: 期望4张,实际{len(image_urls)}张")
|
||||||
|
return None
|
||||||
|
|
||||||
|
logger.info(f"成功完成图片分割,生成4张子图")
|
||||||
return image_urls
|
return image_urls
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"分割图像失败: {str(e)}")
|
logger.error(f"分割图像失败: {str(e)}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|||||||
@ -17,9 +17,9 @@ class Settings(BaseSettings):
|
|||||||
upload_url: str = "http://images.jingrow.com:8080/api/v1/image"
|
upload_url: str = "http://images.jingrow.com:8080/api/v1/image"
|
||||||
|
|
||||||
# 图片保存配置
|
# 图片保存配置
|
||||||
save_dir: str = "../jfile/midjourney"
|
save_dir: str = "../jfile/files"
|
||||||
# Japi 静态资源下载URL
|
# Japi 静态资源下载URL
|
||||||
download_url: str = "http://api.jingrow.com:9080/midjourney"
|
download_url: str = "http://api.jingrow.com:9080/files"
|
||||||
|
|
||||||
# Jingrow Jcloud API 配置
|
# Jingrow Jcloud API 配置
|
||||||
jingrow_api_url: str = "https://cloud.jingrow.com"
|
jingrow_api_url: str = "https://cloud.jingrow.com"
|
||||||
|
|||||||
@ -175,87 +175,6 @@ def is_valid_image_url(url: str) -> bool:
|
|||||||
except:
|
except:
|
||||||
return False
|
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:
|
def get_new_image_url(image_url: str) -> str:
|
||||||
"""将图片URL转换为新的存储URL
|
"""将图片URL转换为新的存储URL
|
||||||
|
|||||||