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