AI 生成: 一个基于Flask的AI一句话生成图片、图标、Logo的Web应用,使用Pollinations AI免费图像生成API。
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# AI Image Generator
|
||||
|
||||
一个基于 Flask 的 AI 图片生成 Web 应用。输入一句话描述,即可生成图片、图标或 Logo。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 🎨 一句话生成图片
|
||||
- 🖼️ 支持多种图片类型(图片 / 图标 / Logo)
|
||||
- 📐 支持多种尺寸(正方形、横向、纵向)
|
||||
- 🎭 支持多种风格(写实、卡通、水彩、赛博朋克、极简等)
|
||||
- 💾 一键下载生成的图片
|
||||
- 🚀 使用免费 API,无需 API Key
|
||||
|
||||
## 技术栈
|
||||
|
||||
- 后端:Python 3.8+ / Flask
|
||||
- 前端:HTML / CSS / JavaScript
|
||||
- 图像生成:Pollinations AI(免费)
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
ai-image-generator/
|
||||
├── app.py # Flask 主程序
|
||||
├── requirements.txt # 依赖列表
|
||||
├── README.md # 说明文档
|
||||
├── templates/
|
||||
│ └── index.html # 前端页面
|
||||
└── static/
|
||||
├── style.css # 样式
|
||||
└── script.js # 前端逻辑
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 安装依赖
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 2. 启动服务
|
||||
|
||||
```bash
|
||||
python app.py
|
||||
```
|
||||
|
||||
### 3. 打开浏览器
|
||||
|
||||
访问 http://127.0.0.1:5000
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 在输入框中输入描述,例如:`一只在星空下奔跑的机械猫`
|
||||
2. 选择图片类型(图片 / 图标 / Logo)
|
||||
3. 选择尺寸和风格
|
||||
4. 点击“生成图片”按钮
|
||||
5. 等待几秒后查看结果,可点击“下载图片”保存
|
||||
|
||||
## API 说明
|
||||
|
||||
### POST /api/generate
|
||||
|
||||
请求体:
|
||||
|
||||
```json
|
||||
{
|
||||
"prompt": "描述文字",
|
||||
"type": "image | icon | logo",
|
||||
"size": "square | landscape | portrait",
|
||||
"style": "realistic | cartoon | watercolor | cyberpunk | minimal"
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"image_url": "https://...",
|
||||
"prompt": "完整提示词"
|
||||
}
|
||||
```
|
||||
|
||||
## 说明
|
||||
|
||||
- 本应用使用 Pollinations AI 的免费图像生成接口,无需注册或 API Key。
|
||||
- 生成速度取决于网络状况,通常 3~10 秒。
|
||||
- 如遇接口不稳定,可稍后重试。
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,105 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
AI 图片生成器 - Flask 后端
|
||||
使用 Pollinations AI 免费接口,一句话生成图片/图标/Logo
|
||||
"""
|
||||
|
||||
import urllib.parse
|
||||
|
||||
import requests
|
||||
from flask import Flask, jsonify, render_template, request
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Pollinations AI 免费图像生成接口
|
||||
POLLINATIONS_URL = "https://image.pollinations.ai/prompt/{prompt}"
|
||||
|
||||
# 图片类型对应的提示词前缀
|
||||
TYPE_PREFIX = {
|
||||
"image": "a high quality image of",
|
||||
"icon": "a clean flat icon of",
|
||||
"logo": "a minimalist logo design of",
|
||||
}
|
||||
|
||||
# 风格对应的提示词后缀
|
||||
STYLE_SUFFIX = {
|
||||
"realistic": "realistic, detailed, 4k, photographic",
|
||||
"cartoon": "cartoon style, colorful, cute, flat illustration",
|
||||
"watercolor": "watercolor painting, soft colors, artistic",
|
||||
"cyberpunk": "cyberpunk style, neon lights, futuristic, sci-fi",
|
||||
"minimal": "minimalist, clean, simple, flat design, white background",
|
||||
}
|
||||
|
||||
# 尺寸映射(宽 x 高)
|
||||
SIZE_MAP = {
|
||||
"square": (1024, 1024),
|
||||
"landscape": (1280, 768),
|
||||
"portrait": (768, 1280),
|
||||
}
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
"""渲染首页"""
|
||||
return render_template("index.html")
|
||||
|
||||
|
||||
@app.route("/api/generate", methods=["POST"])
|
||||
def generate():
|
||||
"""生成图片接口"""
|
||||
data = request.get_json(silent=True) or {}
|
||||
prompt = (data.get("prompt") or "").strip()
|
||||
img_type = data.get("type", "image")
|
||||
size = data.get("size", "square")
|
||||
style = data.get("style", "realistic")
|
||||
|
||||
# 参数校验
|
||||
if not prompt:
|
||||
return jsonify({"success": False, "error": "请输入图片描述"}), 400
|
||||
|
||||
if img_type not in TYPE_PREFIX:
|
||||
img_type = "image"
|
||||
if size not in SIZE_MAP:
|
||||
size = "square"
|
||||
if style not in STYLE_SUFFIX:
|
||||
style = "realistic"
|
||||
|
||||
# 组装完整提示词
|
||||
full_prompt = f"{TYPE_PREFIX[img_type]} {prompt}, {STYLE_SUFFIX[style]}"
|
||||
|
||||
width, height = SIZE_MAP[size]
|
||||
encoded_prompt = urllib.parse.quote(full_prompt)
|
||||
image_url = (
|
||||
f"{POLLINATIONS_URL.format(prompt=encoded_prompt)}"
|
||||
f"?width={width}&height={height}&nologo=true&seed={_random_seed()}"
|
||||
)
|
||||
|
||||
# 预检图片是否可访问(Pollinations 有时会延迟生成)
|
||||
try:
|
||||
head = requests.head(image_url, timeout=30, allow_redirects=True)
|
||||
if head.status_code >= 400:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": f"图像服务返回异常状态:{head.status_code},请稍后重试",
|
||||
}), 502
|
||||
except requests.RequestException as exc:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": f"无法连接图像服务:{exc}",
|
||||
}), 502
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"image_url": image_url,
|
||||
"prompt": full_prompt,
|
||||
})
|
||||
|
||||
|
||||
def _random_seed() -> int:
|
||||
"""生成随机种子,避免相同提示词返回相同图片"""
|
||||
import random
|
||||
return random.randint(1, 10_000_000)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=5000, debug=True)
|
||||
@@ -0,0 +1,2 @@
|
||||
Flask==3.0.3
|
||||
requests==2.32.3
|
||||
@@ -0,0 +1,78 @@
|
||||
// AI 图片生成器 - 前端逻辑
|
||||
|
||||
const promptEl = document.getElementById("prompt");
|
||||
const typeEl = document.getElementById("type");
|
||||
const sizeEl = document.getElementById("size");
|
||||
const styleEl = document.getElementById("style");
|
||||
const generateBtn = document.getElementById("generateBtn");
|
||||
const statusEl = document.getElementById("status");
|
||||
const placeholderEl = document.getElementById("placeholder");
|
||||
const resultImageEl = document.getElementById("resultImage");
|
||||
const actionsEl = document.getElementById("actions");
|
||||
const downloadBtn = document.getElementById("downloadBtn");
|
||||
|
||||
// 设置状态提示
|
||||
function setStatus(message, isError = false) {
|
||||
statusEl.textContent = message || "";
|
||||
statusEl.classList.toggle("error", Boolean(isError));
|
||||
}
|
||||
|
||||
// 显示加载中
|
||||
function setLoading(loading) {
|
||||
generateBtn.disabled = loading;
|
||||
generateBtn.textContent = loading ? "⏳ 生成中..." : "✨ 生成图片";
|
||||
}
|
||||
|
||||
// 调用后端生成图片
|
||||
async function generateImage() {
|
||||
const prompt = promptEl.value.trim();
|
||||
if (!prompt) {
|
||||
setStatus("请先输入图片描述", true);
|
||||
promptEl.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
setStatus("正在生成,请稍候...");
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/generate", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
prompt: prompt,
|
||||
type: typeEl.value,
|
||||
size: sizeEl.value,
|
||||
style: styleEl.value,
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok || !data.success) {
|
||||
throw new Error(data.error || "生成失败,请稍后重试");
|
||||
}
|
||||
|
||||
// 显示结果图
|
||||
resultImageEl.src = data.image_url;
|
||||
resultImageEl.classList.remove("hidden");
|
||||
placeholderEl.classList.add("hidden");
|
||||
actionsEl.classList.remove("hidden");
|
||||
downloadBtn.href = data.image_url;
|
||||
setStatus("生成成功 ✅");
|
||||
} catch (err) {
|
||||
setStatus(err.message || "网络错误,请稍后重试", true);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
generateBtn.addEventListener("click", generateImage);
|
||||
|
||||
// 支持 Ctrl + Enter 快捷生成
|
||||
promptEl.addEventListener("keydown", (e) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
|
||||
generateImage();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,205 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg: #0f1220;
|
||||
--panel: #1a1f35;
|
||||
--panel-2: #232a47;
|
||||
--text: #e8ecff;
|
||||
--muted: #9aa3c7;
|
||||
--primary: #7c5cff;
|
||||
--primary-hover: #6a4bee;
|
||||
--border: #2c3457;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
|
||||
"Microsoft YaHei", sans-serif;
|
||||
background: radial-gradient(circle at top, #1b2140 0%, var(--bg) 60%);
|
||||
color: var(--text);
|
||||
min-height: 100vh;
|
||||
padding: 24px 16px;
|
||||
}
|
||||
|
||||
.app {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 28px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.main {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 860px) {
|
||||
.main {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.field {
|
||||
display: block;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.field-label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
color: var(--muted);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
textarea,
|
||||
select {
|
||||
width: 100%;
|
||||
background: var(--panel-2);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
color: var(--text);
|
||||
padding: 10px 12px;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
outline: none;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
textarea:focus,
|
||||
select:focus {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
width: 100%;
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s, transform 0.1s;
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background: var(--primary-hover);
|
||||
}
|
||||
|
||||
.btn-primary:active:not(:disabled) {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
display: inline-block;
|
||||
background: var(--panel-2);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 10px 16px;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #2c3560;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin-top: 10px;
|
||||
font-size: 13px;
|
||||
color: var(--muted);
|
||||
min-height: 18px;
|
||||
}
|
||||
|
||||
.status.error {
|
||||
color: #ff7b7b;
|
||||
}
|
||||
|
||||
.preview-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 380px;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
text-align: center;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.placeholder-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.result-image {
|
||||
max-width: 100%;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<!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/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<header class="header">
|
||||
<h1>🎨 AI 图片生成器</h1>
|
||||
<p class="subtitle">输入一句话,生成图片 / 图标 / Logo</p>
|
||||
</header>
|
||||
|
||||
<main class="main">
|
||||
<section class="panel form-panel">
|
||||
<label class="field">
|
||||
<span class="field-label">图片描述</span>
|
||||
<textarea
|
||||
id="prompt"
|
||||
rows="3"
|
||||
placeholder="例如:一只在星空下奔跑的机械猫"
|
||||
></textarea>
|
||||
</label>
|
||||
|
||||
<div class="row">
|
||||
<label class="field">
|
||||
<span class="field-label">类型</span>
|
||||
<select id="type">
|
||||
<option value="image">图片</option>
|
||||
<option value="icon">图标</option>
|
||||
<option value="logo">Logo</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span class="field-label">尺寸</span>
|
||||
<select id="size">
|
||||
<option value="square">正方形 1024×1024</option>
|
||||
<option value="landscape">横向 1280×768</option>
|
||||
<option value="portrait">纵向 768×1280</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span class="field-label">风格</span>
|
||||
<select id="style">
|
||||
<option value="realistic">写实</option>
|
||||
<option value="cartoon">卡通</option>
|
||||
<option value="watercolor">水彩</option>
|
||||
<option value="cyberpunk">赛博朋克</option>
|
||||
<option value="minimal">极简</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button id="generateBtn" class="btn-primary">✨ 生成图片</button>
|
||||
<p id="status" class="status"></p>
|
||||
</section>
|
||||
|
||||
<section class="panel preview-panel">
|
||||
<div id="placeholder" class="placeholder">
|
||||
<div class="placeholder-icon">🖼️</div>
|
||||
<p>生成的图片会显示在这里</p>
|
||||
</div>
|
||||
<img id="resultImage" class="result-image hidden" alt="生成结果" />
|
||||
<div id="actions" class="actions hidden">
|
||||
<a id="downloadBtn" class="btn-secondary" download="ai-image.png">⬇️ 下载图片</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<p>Powered by Pollinations AI · 仅供学习交流</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="/static/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user