AI 生成: 一个基于Flask的AI一句话生成图片、图标、Logo的Web应用,使用Pollinations AI免费图像生成API。

This commit is contained in:
admin
2026-09-29 18:09:39 +08:00
commit 50e35d42c0
6 changed files with 564 additions and 0 deletions
+93
View File
@@ -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
+105
View File
@@ -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)
+2
View File
@@ -0,0 +1,2 @@
Flask==3.0.3
requests==2.32.3
+78
View File
@@ -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();
}
});
+205
View File
@@ -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;
}
+81
View File
@@ -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>