C文件夹布局
网页存放/ │ ├── app.py # Flask 应用 ├── templates/ # 存放 HTML 文件 │ ├── index.html # 主页 │ └── upload.html # 详情页 ├── static/ │ ├── css/ │ │ └── styles.css # 样式文件 │ ├── js/ │ │ └── script.js # JavaScript 文件 │ └── images/ # 存放图片 │ ├── 1.png │ ├── 2.png │ └── ... └── uploads/ # 上传文件存储位置 └── processed/ # 处理后的文件存储位置
主页index.html:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AI服务平台</title> <link rel="stylesheet" href="static/css/styles.css"> <script src="../static/js/script.js"></script> </head> <body> <h1 class="main-title">AI应用赋能设计行业</h1> <h2 class="sub-title">作者:zly</h2> <!-- 工业设计分类 --> <h3 class="section-title">工业设计:用AI开一家电商店</h3> <div class="grid-container"> <a href="/detail" target="_self" class="card"> <img class="card-image" src="/static/images/1.png" alt="AI建筑转马克笔手绘"> <div class="card-content"> <h3>AI建筑转马克笔手绘</h3> <p>为成品建筑方案添加一个手绘稿</p> </div> </a> <a href="2.详情应用页:建筑转马克笔/detail2.html" target="_blank" class="card"> <img class="card-image" src="static/images/2.png" alt="AI视觉创作"> <div class="card-content"> <h3>AI视觉创作</h3> <p>一键生成独特的视觉作品和元素</p> </div> </a> <a href="3.详情应用页:建筑转马克笔/detail3.html" target="_blank" class="card"> <img class="card-image" src="static/images/3.png" alt="AI视觉创作"> <div class="card-content"> <h3>AI视觉创作</h3> <p>一键生成独特的视觉作品和元素</p> </div> </a> <a href="4.详情应用页:建筑转马克笔/detail4.html" target="_blank" class="card"> <img class="card-image" src="static/images/4.png" alt="AI视觉创作"> <div class="card-content"> <h3>AI视觉创作</h3> <p>一键生成独特的视觉作品和元素</p> </div> </a> <!-- 复制三次以形成四个卡片 --> <!-- 卡片2 --> <!-- 卡片3 --> <!-- 卡片4 --> </div> <!-- 室内设计分类 --> <h3 class="section-title">建筑设计:AI辅助建筑设计全链</h3> <div class="grid-container"> <a href="5.详情应用页:建筑转马克笔/detail5.html" target="_blank" class="card"> <img class="card-image" src="static/images/1.png" alt="AI视觉创作"> <div class="card-content"> <h3>AI建筑转马克笔手绘</h3> <p>为成品建筑方案添加一个手绘稿</p> </div> </a> <a href="6.详情应用页:建筑转木模型/detail6.html" target="_blank" class="card"> <img class="card-image" src="static/images/6.png" alt="AI视觉创作"> <div class="card-content"> <h3>AI建筑转木模型</h3> <p>为成品建筑方案添加一个手绘稿</p> </div> </a> <a href="7.详情应用页:建筑转马克笔/detail7.html" target="_blank" class="card"> <img class="card-image" src="static/images/7.png" alt="AI视觉创作"> <div class="card-content"> <h3>AI视觉创作</h3> <p>一键生成独特的视觉作品和元素</p> </div> </a> <a href="8.详情应用页:建筑转马克笔/detail8.html" target="_blank" class="card"> <img class="card-image" src="static/images/3.png" alt="AI视觉创作"> <div class="card-content"> <h3>AI视觉创作</h3> <p>一键生成独特的视觉作品和元素</p> </div> </a> <!-- 添加四个卡片,可以复制并修改现有卡片的链接和图片 --> <!-- 确保每个小分类都有四个卡片 --> </div> <!-- 智能文生图应用分类 --> <h3 class="section-title">智能文生图应用:用AI为诗歌、小说、歌词制作画面</h3> <div class="grid-container"> <a href="9.详情应用页:建筑转马克笔/detail9.html" target="_blank" class="card"> <img class="card-image" src="static/images/4.png" alt="AI视觉创作"> <div class="card-content"> <h3>AI视觉创作</h3> <p>一键生成独特的视觉作品和元素</p> </div> </a> <a href="0.详情应用页:建筑转马克笔/detail10.html" target="_blank" class="card"> <img class="card-image" src="static/images/3.png" alt="AI视觉创作"> <div class="card-content"> <h3>AI视觉创作</h3> <p>一键生成独特的视觉作品和元素</p> </div> </a> <a href="11.详情应用页:建筑转马克笔/detail11.html" target="_blank" class="card"> <img class="card-image" src="static/images/6.png" alt="AI视觉创作"> <div class="card-content"> <h3>AI视觉创作</h3> <p>一键生成独特的视觉作品和元素</p> </div> </a> <a href="12.详情应用页:建筑转马克笔/detail12.html" target="_blank" class="card"> <img class="card-image" src="static/images/7.png" alt="AI视觉创作"> <div class="card-content"> <h3>AI视觉创作</h3> <p>一键生成独特的视觉作品和元素</p> </div> </a> <!-- 添加四个卡片,可以复制并修改现有卡片的链接和图片 --> </div> <script src="script.js"></script> </body> </html>
style.css
/* 重置部分默认样式 */ body, html { margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f4f4f4; } /* 主标题样式 */ .main-title { text-align: center; font-size: 28px; font-weight: bold; color: black; margin-top: 30px; margin-bottom: 10px; } /* 副标题样式 */ .sub-title { text-align: center; font-size: 15px; color: gray; margin-bottom: 20px; font-weight: normal; /* 确保副标题不加粗 */ } /* 小标题样式 */ .section-title { font-size: 24px; color: black; margin: 40px 0 20px; text-align: center; } /* 网格布局容器 */ .grid-container { display: grid; grid-template-columns: repeat(4, 1fr); /* 四列布局 */ gap: 20px; /* 网格间距 */ padding: 20px; /* 容器内边距 */ max-width: 1200px; margin: 0 auto; /* 水平居中 */ } /* 卡片样式 */ .card { background-color: #ffffff; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); overflow: hidden; cursor: pointer; transition: transform 0.2s ease-in-out; display: flex; flex-direction: column; align-items: center; text-decoration: none; color: inherit; } .card:hover { transform: scale(1.05); } /* 卡片图片样式 */ .card-image { width: 100%; height: auto; max-height: 150px; object-fit: cover; border-top-left-radius: 10px; border-top-right-radius: 10px; } /* 卡片内容样式 */ .card-content { padding: 15px; text-align: center; flex: 1; } .card h3, .card p { margin: 10px 0; } /* 响应式布局调整 */ @media (max-width: 1024px) { .grid-container { grid-template-columns: repeat(2, 1fr); /* 在较小屏幕上使用两列布局 */ } } @media (max-width: 768px) { .grid-container { grid-template-columns: 1fr; /* 在最小屏幕上使用单列布局 */ padding: 10px; } } /* 小标题样式 */ .section-title { font-size: 20px; /* 字体大小调整 */ font-weight: normal; /* 取消加粗 */ color: black; margin: 40px 0 20px; text-align: center; padding-bottom: 5px; /* 底部填充,为底框留出空间 */ border-bottom: 2px solid #007BFF; /* 添加蓝色的底框 */ width: fit-content; /* 使宽度适应内容 */ margin-left: auto; /* 水平居中 */ margin-right: auto; }
javascript
// 当卡片被点击时调用此函数 function onCardClick(cardIndex) { alert('卡片 ' + cardIndex + ' 被点击'); }
detail.html
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>建筑效果图转彩色手绘</title> <style> body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f2f2f7; color: #1d1d1f; text-align: center; margin: 0; padding: 0; } .container { width: 80%; margin: 40px auto; } .header { margin-bottom: 20px; } .header h1 { font-size: 28px; font-weight: normal; } .header p { font-size: 18px; color: #6e6e73; } .content { display: flex; justify-content: space-around; margin-top: 20px; } .upload-section, .result-section { flex: 1; background: #ffffff; border-radius: 12px; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); padding: 20px; margin: 0 10px; box-sizing: border-box; display: flex; flex-direction: column; justify-content: space-between; } img { max-width: 100%; height: auto; border-radius: 8px; margin-bottom: 20px; } button { padding: 10px 20px; font-size: 16px; border: none; border-radius: 20px; cursor: pointer; transition: background-color 0.3s ease; width: 100%; margin-bottom: 10px; } .red-button { background-color: #ff0000; color: #ffffff; } .red-button:hover { background-color: #cc0000; } .disabled { background-color: #c0c0c0; color: #6e6e73; cursor: not-allowed; } #file-input { display: none; } .loader-container { display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100%; } .loader { border: 6px solid #eeeeee; /* Light grey */ border-top: 6px solid #3498db; /* Blue */ border-radius: 50%; width: 40px; height: 40px; animation: spin 2s linear infinite; } .loading-text { margin-top: 10px; font-size: 16px; color: #3498db; /* Blue */ font-weight: bold; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } </style> </head> <body> <div class="container"> <div class="header"> <h1>建筑效果图转彩色手绘</h1> <p>使用介绍: 请在左侧框内上传一张建筑渲染图</p> </div> <div class="content"> <div class="upload-section"> <input type="file" id="file-input" hidden /> <label for="file-input" class="upload-button"> <img id="original-image" src="/static/images/AI建筑原图.png" alt="AI建筑原图" /> <button id="upload-btn">上传参考图</button> </label> <button id="call-api-btn" class="red-button disabled" disabled>开始生成</button> </div> <div class="result-section"> <img id="sketched-image" src="/static/images/AI建筑手绘.png" alt="AI建筑手绘图" /> </div> </div> </div> <script> const fileInput = document.getElementById('file-input'); const originalImage = document.getElementById('original-image'); const sketchedImage = document.getElementById('sketched-image'); const uploadButton = document.getElementById('upload-btn'); const callApiButton = document.getElementById('call-api-btn'); const resultSection = document.querySelector('.result-section'); fileInput.addEventListener('change', function(event) { const file = event.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = function(e) { originalImage.src = e.target.result; callApiButton.classList.remove('disabled'); callApiButton.disabled = false; }; reader.readAsDataURL(file); } }); uploadButton.addEventListener('click', function() { fileInput.click(); }); callApiButton.addEventListener('click', async function() { this.classList.add('disabled'); this.disabled = true; resultSection.innerHTML = ` <div class="loader-container"> <div class="loader"></div> <div class="loading-text">生成中...</div> </div>`; const formData = new FormData(); formData.append('file', fileInput.files[0]); try { const response = await fetch('/upload', { method: 'POST', body: formData }); const data = await response.json(); if (data.image_url) { const newSrc = data.image_url + "&ts=" + new Date().getTime(); // Avoid browser cache sketchedImage.src = newSrc; resultSection.innerHTML = ''; // Clear the loader resultSection.appendChild(sketchedImage); // Show the new image } else { alert(data.error || '上传或处理失败'); } } catch (error) { console.error('Error:', error); alert('上传或处理失败'); } finally { this.classList.remove('disabled'); this.disabled = false; } }); callApiButton.classList.add('disabled'); callApiButton.disabled = true; // Initially disabled </script> </body> </html>
app.py
from flask import Flask, render_template, request, jsonify, send_from_directory, url_for import os import requests import base64 from PIL import Image import io import random import logging import datetime # 设置日志记录 logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') app = Flask(__name__, static_folder='static') app.config['UPLOAD_FOLDER'] = 'static/uploads/' app.config['PROCESSED_FOLDER'] = 'static/processed/' def encode_image_to_base64(image): """Encode image to base64 string for API consumption.""" buffered = io.BytesIO() image.save(buffered, format="PNG") return base64.b64encode(buffered.getvalue()).decode('utf-8') def save_decoded_image(b64_image, folder_path, image_name): """Decode base64 image string and save it to specified folder.""" image_data = base64.b64decode(b64_image) seq = 0 output_path = os.path.join(folder_path, f"{image_name}.png") while os.path.exists(output_path): seq += 1 output_path = os.path.join(folder_path, f"{image_name}({seq}).png") with open(output_path, 'wb') as image_file: image_file.write(image_data) return output_path @app.route('/') def home(): return render_template('index.html') @app.route('/detail', methods=['GET', 'POST']) def upload(): if request.method == 'GET': return render_template('detail.html') @app.route('/upload', methods=['POST']) def upload_file(): """Handle file upload and image processing.""" if 'file' not in request.files: logging.error("No file part in request") return jsonify({'error': 'No file part'}), 400 file = request.files['file'] if file.filename == '': logging.error("No file selected for uploading") return jsonify({'error': 'No selected file'}), 400 if file: filename = file.filename upload_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) file.save(upload_path) try: with Image.open(upload_path) as img: encoded_image = encode_image_to_base64(img) # 构建 API 请求数据 data = { "prompt": "<lora:CWG_archisketch_v1:1>,Building,pre sketch,masterpiece,best quality,featuring markers,(3D:0.7)", "negative_prompt": "blurry, lower quality, glossy finish,insufficient contrast", "init_images": [encoded_image], "steps": 30, "width": img.width, "height": img.height, "seed": random.randint(1, 10000000), "alwayson_scripts": { "ControlNet": { "args": [ { "enabled": "true", "pixel_perfect": "true", "module": "canny", "model": "control_v11p_sd15_canny_fp16", "weight": 1, "image": encoded_image }, { "enabled": "true", "pixel_perfect": "true", "module": "depth", "model": "control_v11f1p_sd15_depth_fp16", "weight": 1, "image": encoded_image } ] } } } api_url = 'http://127.0.0.1:7860/sdapi/v1/txt2img' # Change to your actual API URL response = requests.post(api_url, json=data) if response.status_code == 200: processed_image_b64 = response.json().get('images')[0] save_path = save_decoded_image(processed_image_b64, app.config['PROCESSED_FOLDER'], "processed_image") timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S") return jsonify({'image_url': url_for('get_image', filename=os.path.basename(save_path), ts=timestamp)}) else: logging.error(f"API request failed with status {response.status_code}") return jsonify({'error': 'Failed to get response from API'}), response.status_code except Exception as e: logging.exception("Failed during image processing or API request") return jsonify({'error': str(e)}), 500 @app.route('/images/<filename>') def get_image(filename): """Serve processed image from directory.""" return send_from_directory(app.config['PROCESSED_FOLDER'], filename) if __name__ == '__main__': # 确保上传和处理后的图片存储文件夹存在 if not os.path.exists(app.config['UPLOAD_FOLDER']): os.makedirs(app.config['UPLOAD_FOLDER']) if not os.path.exists(app.config['PROCESSED_FOLDER']): os.makedirs(app.config['PROCESSED_FOLDER']) # 设置 host='0.0.0.0' 允许从任何 IP 访问 app.run(host='0.0.0.0', port=5000, debug=True) # 将 debug 设置为 False 在生产环境中
标签:动画,主页,color,image,详情页,file,path,margin,app From: https://www.cnblogs.com/zly324/p/18175471