From 7787fdcff7ceeba346e65ef0ce2e4179a165597c Mon Sep 17 00:00:00 2001 From: cm <2962511928@qq.com> Date: Sat, 31 May 2025 11:25:22 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=AC=E4=B8=80=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Person.java => PycharmProjects/__init__.py | 0 PycharmProjects/app.py | 31 ++++ PycharmProjects/config.py | 15 ++ PycharmProjects/templates/__init__.py | 0 PycharmProjects/templates/recommendations.py | 176 +++++++++++++++++++ PycharmProjects/templates/user_info.py | 24 +++ PycharmProjects/utils/__init__.py | 3 + PycharmProjects/utils/image_processing.py | 10 ++ PycharmProjects/utils/outfits.py | 159 +++++++++++++++++ PycharmProjects/utils/weather.py | 34 ++++ 10 files changed, 452 insertions(+) rename Person.java => PycharmProjects/__init__.py (100%) create mode 100644 PycharmProjects/app.py create mode 100644 PycharmProjects/config.py create mode 100644 PycharmProjects/templates/__init__.py create mode 100644 PycharmProjects/templates/recommendations.py create mode 100644 PycharmProjects/templates/user_info.py create mode 100644 PycharmProjects/utils/__init__.py create mode 100644 PycharmProjects/utils/image_processing.py create mode 100644 PycharmProjects/utils/outfits.py create mode 100644 PycharmProjects/utils/weather.py diff --git a/Person.java b/PycharmProjects/__init__.py similarity index 100% rename from Person.java rename to PycharmProjects/__init__.py diff --git a/PycharmProjects/app.py b/PycharmProjects/app.py new file mode 100644 index 0000000..1832370 --- /dev/null +++ b/PycharmProjects/app.py @@ -0,0 +1,31 @@ +import streamlit as st +from templates.user_info import show_user_info_form +from templates.recommendations import show_recommendations_tab +from templates.recommendations import show_virtual_tryon_tab + +# 页面设置 +st.set_page_config(page_title="智能穿搭助手", page_icon="👗", layout="wide") + +# 标题和介绍 +st.title("👗 智能穿搭助手") + +# 初始化session state +if 'user_info' not in st.session_state: + st.session_state.user_info = {} +if 'recommendations' not in st.session_state: + st.session_state.recommendations = None + +# 侧边栏 - 用户信息收集 +show_user_info_form() + +# 主界面 +tab1, tab2 = st.tabs(["穿搭推荐", "虚拟试衣"]) + +with tab1: + show_recommendations_tab() + +with tab2: + show_virtual_tryon_tab() + +if __name__ == "__main__": + st.write("") \ No newline at end of file diff --git a/PycharmProjects/config.py b/PycharmProjects/config.py new file mode 100644 index 0000000..219b4c1 --- /dev/null +++ b/PycharmProjects/config.py @@ -0,0 +1,15 @@ +import os +from dotenv import load_dotenv + +# 中国主要城市中英文对照表 +CITY_NAME_MAPPING = { + # 直辖市 + "beijing": "北京", "peking": "北京", + "shanghai": "上海", + # ... (保持原有的城市映射不变) +} + +# 加载环境变量 +load_dotenv() +MOONSHOT_API_KEY = os.getenv("MOONSHOT_API_KEY") +WEATHER_API_KEY = os.getenv("WEATHER_API_KEY") \ No newline at end of file diff --git a/PycharmProjects/templates/__init__.py b/PycharmProjects/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/PycharmProjects/templates/recommendations.py b/PycharmProjects/templates/recommendations.py new file mode 100644 index 0000000..3ff342a --- /dev/null +++ b/PycharmProjects/templates/recommendations.py @@ -0,0 +1,176 @@ +import streamlit as st +import requests +from PIL import Image + +from utils.weather import get_weather_data +from utils.outfits import generate_sample_outfits +from utils.outfits import generate_fallback_outfits +from utils.image_processing import process_image_for_try_on +from config import MOONSHOT_API_KEY + + +def show_recommendations_tab(): + st.header("智能穿搭推荐") + + with st.form("scene_form"): + col1, col2 = st.columns(2) + with col1: + scene_input = st.text_input("描述你的场景需求", + placeholder="例如: 今天要和朋友们去公园野餐") + with col2: + city_input = st.text_input("输入城市名称", + placeholder="例如: 北京", + value="北京") + + use_weather = st.checkbox("获取当地天气信息", value=True) + submitted = st.form_submit_button("获取穿搭建议") + + if submitted and scene_input: + with st.spinner("正在生成穿搭建议..."): + try: + weather_info = "" + if use_weather and city_input: + weather_data = get_weather_data(city_input) + if weather_data: + weather_info = f""" + ## 当前天气情况 + - 城市: {weather_data['city']} + - 天气状况: {weather_data['description']} + - 温度: {weather_data['temp']}°C + - 湿度: {weather_data['humidity']}% + """ + st.markdown(weather_info) + + prompt = build_prompt(scene_input, weather_info if use_weather else "") + response = call_moonshot_api(prompt) + + if response: + st.session_state.recommendations = response + st.success("穿搭建议生成成功!") + + # 新增:根据建议生成图片 + outfit_image = generate_sample_outfits(response) + if outfit_image: + st.image(outfit_image, caption="推荐穿搭可视化", width=300) + st.session_state.outfit_image = outfit_image + else: + st.warning("无法生成穿搭图片,但文字建议有效") + else: + st.error("获取穿搭建议失败") + + except Exception as e: + st.error(f"获取建议时出错: {str(e)}") + + # recommendations.py 修改后的显示部分 + if st.session_state.recommendations: + st.markdown("### 您的专属穿搭建议") + st.markdown(st.session_state.recommendations) + + st.markdown("### 搭配示例") + try: + sample_images = generate_sample_outfits( + st.session_state.recommendations, + save_to_local=True + ) + + if sample_images: + # 确保只显示实际生成的图片数量 + num_images = min(3, len(sample_images)) + cols = st.columns(num_images) + + for i in range(num_images): + with cols[i]: + st.image( + sample_images[i], + use_container_width=True, + caption=f"穿搭方案 {i + 1}" + ) + st.success(f"示例 {i + 1}") + else: + st.warning("无法生成任何穿搭示例图片") + + except Exception as e: + st.error(f"生成穿搭示例时出错: {str(e)}") + # 显示回退方案 + fallback_images = generate_fallback_outfits(save_to_local=True) + cols = st.columns(3) + for i, col in enumerate(cols): + col.image( + fallback_images[i], + use_container_width=True, + caption=f"穿搭方案 {i + 1} (示例)" + ) + + + +def build_prompt(scene_input, weather_info): + return f""" + 你是一位专业的时尚穿搭助手,请根据以下信息为用户提供穿搭建议: + + ### 用户信息 + {st.session_state.user_info} + + ### 场景描述 + {scene_input} + + ### 天气信息 + {weather_info if weather_info else "用户未提供天气信息"} + + ### 请提供以下内容: + 1. 适合该场景的3套穿搭方案 + 2. 每套方案的风格特点和适用场合说明 + 3. 颜色搭配建议 + 4. 材质和面料选择建议 + 5. 购买建议和品牌推荐(可选) + """ + + +def call_moonshot_api(prompt): + headers = { + "Authorization": f"Bearer {MOONSHOT_API_KEY}", + "Content-Type": "application/json" + } + + data = { + "model": "moonshot-v1-128k", + "messages": [ + { + "role": "system", + "content": "你是一位专业的时尚顾问,擅长根据用户特征、场景和天气状况提供个性化的穿搭建议。" + }, + { + "role": "user", + "content": prompt + } + ], + "temperature": 0.7, + "max_tokens": 2000 + } + + response = requests.post( + "https://api.moonshot.cn/v1/chat/completions", + headers=headers, + json=data + ) + + if response.status_code == 200: + result = response.json() + return result["choices"][0]["message"]["content"] + else: + st.error(f"API调用失败: {response.text}") + return None + + +def show_virtual_tryon_tab(): + st.header("虚拟试衣间") + st.info("此功能正在开发中,即将推出!") + + uploaded_file = st.file_uploader("上传你的照片进行虚拟试衣", type=["jpg", "png", "jpeg"]) + if uploaded_file is not None: + image = Image.open(uploaded_file) + st.image(image, caption="上传的照片", use_container_width=True) + + gray, edges = process_image_for_try_on(image) + col1, col2 = st.columns(2) + col1.image(gray, caption="灰度处理", use_container_width=True, channels="GRAY") + col2.image(edges, caption="边缘检测", use_container_width=True, channels="GRAY") \ No newline at end of file diff --git a/PycharmProjects/templates/user_info.py b/PycharmProjects/templates/user_info.py new file mode 100644 index 0000000..b654389 --- /dev/null +++ b/PycharmProjects/templates/user_info.py @@ -0,0 +1,24 @@ +import streamlit as st + +def show_user_info_form(): + with st.sidebar: + st.header("个人信息") + with st.form("user_info_form"): + gender = st.selectbox("性别", ["女", "男", "其他"]) + age = st.slider("年龄", 10, 80, 25) + height = st.number_input("身高(cm)", 100, 250, 165) + weight = st.number_input("体重(kg)", 30, 150, 55) + skin_tone = st.selectbox("肤色", ["白皙", "自然", "小麦色", "深色"]) + style_preference = st.multiselect("风格偏好", ["休闲", "正式", "运动", "甜美", "复古", "街头", "商务"]) + submitted = st.form_submit_button("保存信息") + + if submitted: + st.session_state.user_info = { + "gender": gender, + "age": age, + "height": height, + "weight": weight, + "skin_tone": skin_tone, + "style_preference": style_preference + } + st.success("个人信息已保存!") \ No newline at end of file diff --git a/PycharmProjects/utils/__init__.py b/PycharmProjects/utils/__init__.py new file mode 100644 index 0000000..bd07aef --- /dev/null +++ b/PycharmProjects/utils/__init__.py @@ -0,0 +1,3 @@ +from .weather import get_weather_data +from .outfits import generate_sample_outfits +from .image_processing import process_image_for_try_on \ No newline at end of file diff --git a/PycharmProjects/utils/image_processing.py b/PycharmProjects/utils/image_processing.py new file mode 100644 index 0000000..7079c1b --- /dev/null +++ b/PycharmProjects/utils/image_processing.py @@ -0,0 +1,10 @@ +import cv2 +import numpy as np +from PIL import Image + +def process_image_for_try_on(image): + """处理上传的试衣图片""" + img_array = np.array(image) + gray = cv2.cvtColor(img_array, cv2.COLOR_BGR2GRAY) + edges = cv2.Canny(gray, 100, 200) + return gray, edges \ No newline at end of file diff --git a/PycharmProjects/utils/outfits.py b/PycharmProjects/utils/outfits.py new file mode 100644 index 0000000..c732d6e --- /dev/null +++ b/PycharmProjects/utils/outfits.py @@ -0,0 +1,159 @@ +import numpy as np +import matplotlib.pyplot as plt +import cv2 +from io import BytesIO +from PIL import Image +import streamlit as st + +try: + import tensorflow as tf + from tensorflow.keras.datasets import fashion_mnist + + HAS_TF = True +except ImportError: + HAS_TF = False + +import os +from datetime import datetime + + +def map_keyword_to_indices(text, class_names): + """私有方法:从推荐文本解析服装类别索引""" + keyword_map = { + # 中文关键词映射 + "t恤": 0, "衬衫": 6, "外套": 4, "套头衫": 2, + "裤子": 1, "牛仔裤": 1, + "连衣裙": 3, "裙子": 3, + "凉鞋": 5, "运动鞋": 7, "短靴": 9, "靴子": 9 + } + + matched_indices = [] + text_lower = text.lower() + for keyword, idx in keyword_map.items(): + if keyword in text_lower: + matched_indices.append(idx) + return list(set(matched_indices)) # 去重 + + +# outfits.py 修改后的代码 +def generate_sample_outfits(recommendation_text, save_to_local=False, output_dir="C:/Users/Administrator/Desktop"): + """生成示例穿搭图片(包含连衣裙选项)并可选保存到本地""" + outfits = [] + + # 创建输出目录 + if save_to_local and not os.path.exists(output_dir): + os.makedirs(output_dir) + + if HAS_TF: + try: + (train_images, train_labels), _ = fashion_mnist.load_data() + class_names = ['T恤', '裤子', '套头衫', '连衣裙', '外套', + '凉鞋', '衬衫', '运动鞋', '包', '短靴'] + + # 从推荐文本中提取服装类别索引 + matched_indices = map_keyword_to_indices(recommendation_text, class_names) + + # 根据匹配结果生成图片 + if not matched_indices: + st.warning("无法从推荐文本中识别有效服装类别") + return generate_fallback_outfits(save_to_local, output_dir) + + # 生成3套穿搭方案 + for i in range(3): + try: + # 随机决定是生成连衣裙组合还是上下装组合 + use_dress = np.random.choice([True, False]) and (3 in matched_indices) + + if use_dress: + # 连衣裙+鞋子组合 + shoes_indices = [i for i in matched_indices if i in [5, 7, 9]] + if not shoes_indices: + continue + + shoes_idx = np.random.choice(shoes_indices) + dress_img = train_images[np.random.choice(np.where(train_labels == 3)[0])] + shoes_img = train_images[np.random.choice(np.where(train_labels == shoes_idx)[0])] + + dress_img = cv2.resize(dress_img, (128, 256), interpolation=cv2.INTER_NEAREST) + shoes_img = cv2.resize(shoes_img, (128, 128), interpolation=cv2.INTER_NEAREST) + combined = np.vstack([dress_img, shoes_img]) + combined_rgb = np.stack([combined] * 3, axis=-1) + + title = f"{class_names[3]}+{class_names[shoes_idx]}" + else: + # 上下装+鞋子组合 + top_indices = [i for i in matched_indices if i in [0, 2, 4, 6]] + bottom_indices = [i for i in matched_indices if i in [1]] + shoes_indices = [i for i in matched_indices if i in [5, 7, 9]] + + if not top_indices or not bottom_indices or not shoes_indices: + continue + + top_idx = np.random.choice(top_indices) + bottom_idx = np.random.choice(bottom_indices) + shoes_idx = np.random.choice(shoes_indices) + + top_img = train_images[np.random.choice(np.where(train_labels == top_idx)[0])] + bottom_img = train_images[np.random.choice(np.where(train_labels == bottom_idx)[0])] + shoes_img = train_images[np.random.choice(np.where(train_labels == shoes_idx)[0])] + + top_img = cv2.resize(top_img, (128, 128), interpolation=cv2.INTER_NEAREST) + bottom_img = cv2.resize(bottom_img, (128, 128), interpolation=cv2.INTER_NEAREST) + shoes_img = cv2.resize(shoes_img, (128, 128), interpolation=cv2.INTER_NEAREST) + combined = np.vstack([top_img, bottom_img, shoes_img]) + combined_rgb = np.stack([combined] * 3, axis=-1) + + title = f"{class_names[top_idx]}+{class_names[bottom_idx]}+{class_names[shoes_idx]}" + + # 直接使用PIL保存图片,避免plt保存问题 + img = Image.fromarray(combined_rgb) + + if save_to_local: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"{output_dir}/outfit_{timestamp}_{i}.png" + img.save(filename, format='PNG', quality=95) + + outfits.append(img) + + except Exception as e: + st.warning(f"生成第{i + 1}套穿搭图片时出错: {str(e)}") + outfits.append(generate_single_fallback_outfit(i + 1, save_to_local, output_dir)) + continue + + return outfits if outfits else generate_fallback_outfits(save_to_local, output_dir) + + except Exception as e: + st.warning(f"生成服装图片出错: {str(e)},使用文字替代") + return generate_fallback_outfits(save_to_local, output_dir) + + return generate_fallback_outfits(save_to_local, output_dir) + + +def generate_single_fallback_outfit(index, save_to_local=False, output_dir=None): + """生成单个回退的文字穿搭方案""" + fig, ax = plt.subplots(figsize=(3, 4)) + ax.set_facecolor('#f0f2f6') + ax.text(0.5, 0.5, f"穿搭方案 {index}\n(图片生成失败)", + ha='center', va='center', fontsize=10) + ax.axis('off') + + buf = BytesIO() + fig.savefig(buf, format='png', bbox_inches='tight', pad_inches=0.1, dpi=100) + buf.seek(0) + img = Image.open(buf) + plt.close(fig) + + if save_to_local and output_dir: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"{output_dir}/outfit_{timestamp}_{index}_fallback.png" + img.save(filename) + + return img + + +def generate_fallback_outfits(save_to_local=False, output_dir="C:/Users/Administrator/Desktop"): + """生成回退的文字穿搭方案""" + outfits = [] + for i in range(3): + outfits.append(generate_single_fallback_outfit(i + 1, save_to_local, output_dir)) + return outfits \ No newline at end of file diff --git a/PycharmProjects/utils/weather.py b/PycharmProjects/utils/weather.py new file mode 100644 index 0000000..ddaac0e --- /dev/null +++ b/PycharmProjects/utils/weather.py @@ -0,0 +1,34 @@ +import requests +from config import CITY_NAME_MAPPING, WEATHER_API_KEY + +def get_weather_data(city, api_key=WEATHER_API_KEY): + """获取天气数据""" + base_url = "http://api.openweathermap.org/data/2.5/weather" + params = { + "q": city, + "appid": api_key, + "units": "metric", + "lang": "zh_cn" + } + + try: + response = requests.get(base_url, params=params, timeout=10) + data = response.json() + + if response.status_code == 200: + normalized_input = city.lower().replace(" ", "") + chinese_name = CITY_NAME_MAPPING.get(normalized_input, data.get("name", city)) + + return { + "city": chinese_name, + "temp": data["main"]["temp"], + "humidity": data["main"]["humidity"], + "description": data["weather"][0]["description"], + "icon": data["weather"][0]["icon"] + } + else: + return None + + except Exception as e: + print(f"获取天气数据时出错: {str(e)}") + return None \ No newline at end of file