📁 重组文档目录结构

变更:
- 创建 docs/ 目录统一管理所有文档
- 移动所有 API 文档到 docs/ 目录
  - API_DOCS_INDEX.md
  - RESTFUL_API_DOCUMENTATION.md
  - API_SERVICE_README.md
  - API_CLIENT_EXAMPLES.md
  - API_SERVICE_GUIDE.md
  - BRANCH_README.md
  - openapi.yaml
  - IOPaint_API.postman_collection.json
  - UPGRADE_NOTES.md

- 更新所有文档间的引用路径
- 更新 README.md 中的文档链接
- 创建 docs/README.md 作为文档入口

优势:
 清晰的目录结构
 文档集中管理
 易于查找和维护
 符合项目规范

🔧 Generated with Claude Code
This commit is contained in:
let5sne
2025-11-28 18:04:26 +00:00
parent b2580c8626
commit b6ac3f022a
12 changed files with 90 additions and 13 deletions

776
docs/API_CLIENT_EXAMPLES.md Normal file
View File

@@ -0,0 +1,776 @@
# IOPaint API 客户端示例
本文档提供多种编程语言的API调用示例。
## 目录
- [Python](#python)
- [JavaScript/Node.js](#javascriptnodejs)
- [cURL](#curl)
- [PHP](#php)
- [Java](#java)
- [Go](#go)
---
## 配置信息
```bash
API_URL=http://localhost:8080
API_KEY=your_secret_key_change_me
```
---
## Python
### 基础示例
```python
import requests
def remove_watermark(image_path, mask_path=None, api_key="your_secret_key_change_me"):
"""去除图片水印"""
url = "http://localhost:8080/api/v1/remove-watermark"
headers = {"X-API-Key": api_key}
files = {
"image": open(image_path, "rb")
}
if mask_path:
files["mask"] = open(mask_path, "rb")
response = requests.post(url, headers=headers, files=files)
if response.status_code == 200:
# 保存结果
output_path = "result.png"
with open(output_path, "wb") as f:
f.write(response.content)
print(f"✓ 处理成功!结果已保存到: {output_path}")
print(f"处理时间: {response.headers.get('X-Processing-Time')}")
return output_path
else:
print(f"✗ 处理失败: {response.status_code}")
print(response.json())
return None
# 使用示例
remove_watermark("input.jpg")
```
### 高级示例(含错误处理和重试)
```python
import requests
import time
from pathlib import Path
class IOPaintClient:
"""IOPaint API客户端"""
def __init__(self, api_url="http://localhost:8080", api_key=None):
self.api_url = api_url.rstrip("/")
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({"X-API-Key": api_key})
def health_check(self):
"""健康检查"""
response = self.session.get(f"{self.api_url}/api/v1/health")
return response.json()
def get_stats(self):
"""获取使用统计"""
response = self.session.get(f"{self.api_url}/api/v1/stats")
return response.json()
def remove_watermark(
self,
image_path,
mask_path=None,
output_path=None,
max_retries=3,
timeout=120
):
"""
去除图片水印
参数:
image_path: 输入图片路径
mask_path: 遮罩图片路径(可选)
output_path: 输出路径可选默认为input_result.png
max_retries: 最大重试次数
timeout: 超时时间(秒)
返回:
成功返回输出路径失败返回None
"""
# 准备文件
files = {"image": open(image_path, "rb")}
if mask_path:
files["mask"] = open(mask_path, "rb")
# 确定输出路径
if output_path is None:
input_path = Path(image_path)
output_path = input_path.parent / f"{input_path.stem}_result.png"
# 重试逻辑
for attempt in range(max_retries):
try:
response = self.session.post(
f"{self.api_url}/api/v1/remove-watermark",
files=files,
timeout=timeout
)
if response.status_code == 200:
# 保存结果
with open(output_path, "wb") as f:
f.write(response.content)
print(f"✓ 处理成功!")
print(f" 输出: {output_path}")
print(f" 处理时间: {response.headers.get('X-Processing-Time')}")
print(f" 图片尺寸: {response.headers.get('X-Image-Size')}")
return str(output_path)
elif response.status_code == 429:
# 限流,等待后重试
wait_time = 2 ** attempt
print(f"⚠ 请求过于频繁,等待{wait_time}秒后重试...")
time.sleep(wait_time)
continue
else:
# 其他错误
print(f"✗ 处理失败 ({response.status_code})")
error_data = response.json()
print(f" 错误: {error_data.get('detail', '未知错误')}")
return None
except requests.Timeout:
print(f"⚠ 请求超时 (尝试 {attempt + 1}/{max_retries})")
if attempt < max_retries - 1:
continue
else:
print("✗ 超过最大重试次数")
return None
except Exception as e:
print(f"✗ 发生错误: {e}")
return None
finally:
# 关闭文件
for f in files.values():
if hasattr(f, 'close'):
f.close()
return None
def batch_process(self, image_dir, output_dir=None, mask_dir=None):
"""
批量处理图片
参数:
image_dir: 输入图片目录
output_dir: 输出目录(可选)
mask_dir: 遮罩目录(可选,按文件名匹配)
"""
image_dir = Path(image_dir)
if output_dir:
output_dir = Path(output_dir)
output_dir.mkdir(exist_ok=True, parents=True)
# 支持的图片格式
image_exts = {".jpg", ".jpeg", ".png", ".webp"}
images = [
f for f in image_dir.iterdir()
if f.suffix.lower() in image_exts
]
print(f"找到 {len(images)} 张图片")
results = {"success": 0, "failed": 0}
for i, image_path in enumerate(images, 1):
print(f"\n[{i}/{len(images)}] 处理: {image_path.name}")
# 查找对应的遮罩
mask_path = None
if mask_dir:
mask_path = Path(mask_dir) / image_path.name
if not mask_path.exists():
mask_path = None
# 确定输出路径
if output_dir:
out_path = output_dir / f"{image_path.stem}_result.png"
else:
out_path = image_path.parent / f"{image_path.stem}_result.png"
# 处理
result = self.remove_watermark(image_path, mask_path, out_path)
if result:
results["success"] += 1
else:
results["failed"] += 1
# 总结
print("\n" + "=" * 60)
print(f"批量处理完成!")
print(f" 成功: {results['success']}")
print(f" 失败: {results['failed']}")
print("=" * 60)
# 使用示例
if __name__ == "__main__":
# 创建客户端
client = IOPaintClient(
api_url="http://localhost:8080",
api_key="your_secret_key_change_me"
)
# 健康检查
print("健康检查:", client.health_check())
# 单张图片处理
client.remove_watermark("test.jpg")
# 批量处理
client.batch_process("./input_images", "./output_images")
```
---
## JavaScript/Node.js
### 使用 axios
```javascript
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
async function removeWatermark(imagePath, maskPath = null, apiKey = 'your_secret_key_change_me') {
const url = 'http://localhost:8080/api/v1/remove-watermark';
const formData = new FormData();
formData.append('image', fs.createReadStream(imagePath));
if (maskPath) {
formData.append('mask', fs.createReadStream(maskPath));
}
try {
const response = await axios.post(url, formData, {
headers: {
'X-API-Key': apiKey,
...formData.getHeaders()
},
responseType: 'arraybuffer',
timeout: 120000 // 120秒超时
});
// 保存结果
const outputPath = 'result.png';
fs.writeFileSync(outputPath, response.data);
console.log('✓ 处理成功!');
console.log(` 输出: ${outputPath}`);
console.log(` 处理时间: ${response.headers['x-processing-time']}`);
return outputPath;
} catch (error) {
if (error.response) {
console.error('✗ 处理失败:', error.response.status);
console.error(' 错误:', error.response.data.toString());
} else {
console.error('✗ 请求失败:', error.message);
}
return null;
}
}
// 使用示例
removeWatermark('input.jpg');
```
### 完整客户端类
```javascript
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const path = require('path');
class IOPaintClient {
constructor(apiUrl = 'http://localhost:8080', apiKey = null) {
this.apiUrl = apiUrl.replace(/\/$/, '');
this.apiKey = apiKey;
this.client = axios.create({
baseURL: this.apiUrl,
headers: {
'X-API-Key': apiKey
},
timeout: 120000
});
}
async healthCheck() {
const response = await this.client.get('/api/v1/health');
return response.data;
}
async getStats() {
const response = await this.client.get('/api/v1/stats');
return response.data;
}
async removeWatermark(imagePath, maskPath = null, outputPath = null) {
const formData = new FormData();
formData.append('image', fs.createReadStream(imagePath));
if (maskPath) {
formData.append('mask', fs.createReadStream(maskPath));
}
// 确定输出路径
if (!outputPath) {
const parsed = path.parse(imagePath);
outputPath = path.join(parsed.dir, `${parsed.name}_result.png`);
}
try {
const response = await this.client.post('/api/v1/remove-watermark', formData, {
headers: formData.getHeaders(),
responseType: 'arraybuffer'
});
fs.writeFileSync(outputPath, response.data);
console.log('✓ 处理成功!');
console.log(` 输出: ${outputPath}`);
console.log(` 处理时间: ${response.headers['x-processing-time']}`);
return outputPath;
} catch (error) {
if (error.response) {
console.error('✗ 处理失败:', error.response.status);
} else {
console.error('✗ 请求失败:', error.message);
}
return null;
}
}
}
// 使用示例
(async () => {
const client = new IOPaintClient('http://localhost:8080', 'your_secret_key_change_me');
// 健康检查
const health = await client.healthCheck();
console.log('健康检查:', health);
// 处理图片
await client.removeWatermark('test.jpg');
})();
```
---
## cURL
### 基础使用
```bash
# 简单调用
curl -X POST http://localhost:8080/api/v1/remove-watermark \
-H "X-API-Key: your_secret_key_change_me" \
-F "image=@input.jpg" \
-o result.png
# 带遮罩
curl -X POST http://localhost:8080/api/v1/remove-watermark \
-H "X-API-Key: your_secret_key_change_me" \
-F "image=@input.jpg" \
-F "mask=@mask.png" \
-o result.png
# 显示详细信息
curl -X POST http://localhost:8080/api/v1/remove-watermark \
-H "X-API-Key: your_secret_key_change_me" \
-F "image=@input.jpg" \
-o result.png \
-v
# 健康检查
curl http://localhost:8080/api/v1/health
```
### Bash脚本批量处理
```bash
#!/bin/bash
API_URL="http://localhost:8080/api/v1/remove-watermark"
API_KEY="your_secret_key_change_me"
INPUT_DIR="./input"
OUTPUT_DIR="./output"
mkdir -p "$OUTPUT_DIR"
for image in "$INPUT_DIR"/*.{jpg,jpeg,png}; do
[ -f "$image" ] || continue
filename=$(basename "$image")
name="${filename%.*}"
output="$OUTPUT_DIR/${name}_result.png"
echo "处理: $filename"
curl -X POST "$API_URL" \
-H "X-API-Key: $API_KEY" \
-F "image=@$image" \
-o "$output" \
-s -w "状态码: %{http_code}, 时间: %{time_total}s\n"
done
echo "批量处理完成!"
```
---
## PHP
```php
<?php
class IOPaintClient {
private $apiUrl;
private $apiKey;
public function __construct($apiUrl = 'http://localhost:8080', $apiKey = null) {
$this->apiUrl = rtrim($apiUrl, '/');
$this->apiKey = $apiKey;
}
public function healthCheck() {
$ch = curl_init($this->apiUrl . '/api/v1/health');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
public function removeWatermark($imagePath, $maskPath = null, $outputPath = null) {
$url = $this->apiUrl . '/api/v1/remove-watermark';
// 准备文件
$postData = [
'image' => new CURLFile($imagePath)
];
if ($maskPath) {
$postData['mask'] = new CURLFile($maskPath);
}
// 确定输出路径
if (!$outputPath) {
$pathInfo = pathinfo($imagePath);
$outputPath = $pathInfo['dirname'] . '/' . $pathInfo['filename'] . '_result.png';
}
// 发送请求
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . $this->apiKey
]);
curl_setopt($ch, CURLOPT_TIMEOUT, 120);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode == 200) {
file_put_contents($outputPath, $response);
echo "✓ 处理成功!输出: $outputPath\n";
return $outputPath;
} else {
echo "✗ 处理失败 (HTTP $httpCode)\n";
return null;
}
}
}
// 使用示例
$client = new IOPaintClient('http://localhost:8080', 'your_secret_key_change_me');
// 健康检查
print_r($client->healthCheck());
// 处理图片
$client->removeWatermark('test.jpg');
?>
```
---
## Java
```java
import okhttp3.*;
import java.io.*;
import java.nio.file.*;
public class IOPaintClient {
private final String apiUrl;
private final String apiKey;
private final OkHttpClient client;
public IOPaintClient(String apiUrl, String apiKey) {
this.apiUrl = apiUrl.replaceAll("/$", "");
this.apiKey = apiKey;
this.client = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS)
.build();
}
public String removeWatermark(String imagePath, String maskPath, String outputPath) throws IOException {
// 构建请求
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("image", "image.jpg",
RequestBody.create(new File(imagePath), MediaType.parse("image/*")));
if (maskPath != null) {
builder.addFormDataPart("mask", "mask.png",
RequestBody.create(new File(maskPath), MediaType.parse("image/*")));
}
RequestBody requestBody = builder.build();
Request request = new Request.Builder()
.url(apiUrl + "/api/v1/remove-watermark")
.addHeader("X-API-Key", apiKey)
.post(requestBody)
.build();
// 发送请求
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
// 保存结果
if (outputPath == null) {
Path path = Paths.get(imagePath);
String name = path.getFileName().toString();
name = name.substring(0, name.lastIndexOf('.'));
outputPath = path.getParent().resolve(name + "_result.png").toString();
}
try (InputStream is = response.body().byteStream();
FileOutputStream fos = new FileOutputStream(outputPath)) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
System.out.println("✓ 处理成功!输出: " + outputPath);
return outputPath;
} else {
System.err.println("✗ 处理失败: " + response.code());
System.err.println(response.body().string());
return null;
}
}
}
public static void main(String[] args) {
IOPaintClient client = new IOPaintClient(
"http://localhost:8080",
"your_secret_key_change_me"
);
try {
client.removeWatermark("test.jpg", null, null);
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
---
## Go
```go
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"time"
)
type IOPaintClient struct {
apiURL string
apiKey string
client *http.Client
}
func NewIOPaintClient(apiURL, apiKey string) *IOPaintClient {
return &IOPaintClient{
apiURL: apiURL,
apiKey: apiKey,
client: &http.Client{
Timeout: 120 * time.Second,
},
}
}
func (c *IOPaintClient) RemoveWatermark(imagePath, maskPath, outputPath string) error {
// 准备multipart请求
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
// 添加图片
imageFile, err := os.Open(imagePath)
if err != nil {
return err
}
defer imageFile.Close()
imagePart, err := writer.CreateFormFile("image", filepath.Base(imagePath))
if err != nil {
return err
}
io.Copy(imagePart, imageFile)
// 添加遮罩(如果有)
if maskPath != "" {
maskFile, err := os.Open(maskPath)
if err != nil {
return err
}
defer maskFile.Close()
maskPart, err := writer.CreateFormFile("mask", filepath.Base(maskPath))
if err != nil {
return err
}
io.Copy(maskPart, maskFile)
}
writer.Close()
// 创建请求
req, err := http.NewRequest("POST", c.apiURL+"/api/v1/remove-watermark", body)
if err != nil {
return err
}
req.Header.Set("X-API-Key", c.apiKey)
req.Header.Set("Content-Type", writer.FormDataContentType())
// 发送请求
resp, err := c.client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// 检查响应
if resp.StatusCode != 200 {
return fmt.Errorf("请求失败: %d", resp.StatusCode)
}
// 确定输出路径
if outputPath == "" {
ext := filepath.Ext(imagePath)
name := imagePath[:len(imagePath)-len(ext)]
outputPath = name + "_result.png"
}
// 保存结果
outFile, err := os.Create(outputPath)
if err != nil {
return err
}
defer outFile.Close()
_, err = io.Copy(outFile, resp.Body)
if err != nil {
return err
}
fmt.Printf("✓ 处理成功!输出: %s\n", outputPath)
fmt.Printf(" 处理时间: %s秒\n", resp.Header.Get("X-Processing-Time"))
return nil
}
func main() {
client := NewIOPaintClient("http://localhost:8080", "your_secret_key_change_me")
err := client.RemoveWatermark("test.jpg", "", "")
if err != nil {
fmt.Printf("错误: %v\n", err)
}
}
```
---
## 错误处理
### 常见错误代码
| 状态码 | 错误 | 解决方案 |
|--------|------|----------|
| 401 | 未授权 | 检查API Key是否正确 |
| 400 | 请求错误 | 检查图片格式、大小是否符合要求 |
| 429 | 请求过多 | 降低请求频率,稍后重试 |
| 500 | 服务器错误 | 检查服务器日志,联系技术支持 |
| 503 | 服务不可用 | 服务器可能正在重启,稍后重试 |
### 限流说明
默认限流每秒10个请求突发20个请求
如果遇到429错误建议
1. 使用指数退避重试策略
2. 减少并发请求数
3. 联系管理员增加配额
---
## 性能优化建议
1. **批量处理**合理控制并发数建议2-4个并发
2. **图片预处理**:压缩大图片后再上传
3. **复用连接**使用HTTP keep-alive
4. **错误重试**:实现指数退避策略
5. **超时设置**:根据图片大小设置合理超时时间
---
## 支持
如有问题,请访问:
- 文档:`http://localhost:8080/docs`
- GitHubhttps://github.com/let5sne/IOPaint/issues

429
docs/API_DOCS_INDEX.md Normal file
View File

@@ -0,0 +1,429 @@
# IOPaint API 文档导航
本项目提供完整的 API 文档,适用于不同场景和需求。
## 📚 文档总览
| 文档 | 用途 | 适合人群 |
|------|------|---------|
| [API_SERVICE_README.md](./API_SERVICE_README.md) | 快速开始指南 | 所有用户 ⭐ |
| [RESTFUL_API_DOCUMENTATION.md](./RESTFUL_API_DOCUMENTATION.md) | 完整 REST API 文档 | 开发者 ⭐⭐⭐ |
| [API_CLIENT_EXAMPLES.md](./API_CLIENT_EXAMPLES.md) | 多语言客户端示例 | 集成开发者 ⭐⭐ |
| [API_SERVICE_GUIDE.md](./API_SERVICE_GUIDE.md) | 商业化部署方案 | 架构师/CTO ⭐⭐⭐ |
| [openapi.yaml](./openapi.yaml) | OpenAPI 规范 | 工具/自动化 ⭐⭐ |
| [IOPaint_API.postman_collection.json](./IOPaint_API.postman_collection.json) | Postman 集合 | API 测试 ⭐⭐ |
---
## 🎯 按场景选择文档
### 场景 1: 我想快速开始使用 API
**推荐文档**: [API_SERVICE_README.md](./API_SERVICE_README.md)
**内容包括**:
- 3 步启动服务
- API 使用示例
- 常见问题解决
**快速开始**:
```bash
# 1. 设置 API 密钥
export API_KEY="your_secret_key"
# 2. 启动服务
docker-compose -f docker-compose.mvp.yml up -d
# 3. 测试 API
curl -X POST http://localhost:8080/api/v1/remove-watermark \
-H "X-API-Key: $API_KEY" \
-F "image=@test.jpg" \
-o result.png
```
---
### 场景 2: 我需要集成 API 到我的应用
**推荐文档**:
1. [RESTFUL_API_DOCUMENTATION.md](./RESTFUL_API_DOCUMENTATION.md) - 完整 API 参考
2. [API_CLIENT_EXAMPLES.md](./API_CLIENT_EXAMPLES.md) - 代码示例
**支持的语言**:
- ✅ Python
- ✅ JavaScript/Node.js
- ✅ PHP
- ✅ Go
- ✅ Java
- ✅ cURL/Bash
**关键内容**:
- 认证方式
- 所有 API 端点
- 请求/响应格式
- 错误处理
- 限流规则
- 完整代码示例
---
### 场景 3: 我想测试 API
**推荐工具**:
1. [Postman Collection](./IOPaint_API.postman_collection.json) - 一键导入测试
2. Swagger UI - 在线交互式文档
**Postman 使用**:
1. 打开 Postman
2. Import → 选择 `IOPaint_API.postman_collection.json`
3. 设置环境变量 `api_key`
4. 发送请求测试
**Swagger UI 使用**:
```bash
# 启动服务后访问
http://localhost:8080/docs
```
---
### 场景 4: 我要自动生成客户端代码
**推荐文档**: [openapi.yaml](./openapi.yaml)
**支持的工具**:
- OpenAPI Generator
- Swagger Codegen
- Postman (导入 OpenAPI)
**示例**:
```bash
# 使用 OpenAPI Generator 生成 Python 客户端
openapi-generator-cli generate \
-i openapi.yaml \
-g python \
-o ./python-client
# 生成 JavaScript 客户端
openapi-generator-cli generate \
-i openapi.yaml \
-g javascript \
-o ./js-client
```
---
### 场景 5: 我要规划商业化部署
**推荐文档**: [API_SERVICE_GUIDE.md](./API_SERVICE_GUIDE.md)
**内容包括**:
- MVP 最小可行产品方案
- 商业化架构设计(单机 → K8s
- 成本分析和收益模型
- 实施路线图
- 部署方案对比
**关键决策参考**:
| 月处理量 | 推荐方案 | 成本/月 |
|---------|---------|---------|
| < 10万张 | Docker 单机 | ¥300-500 |
| 10-50万张 | Docker Compose | ¥1000-3000 |
| 50万+张 | Kubernetes | ¥5000-20000 |
---
## 📖 文档详细说明
### 1. API_SERVICE_README.md
**快速开始指南** - 10分钟上手
```
内容:
✓ 快速部署3步
✓ API 基础使用
✓ 配置说明
✓ 性能基准
✓ 故障排查
✓ 实施路线图
适合:
• 第一次使用的用户
• 需要快速验证的团队
• POC 阶段
```
---
### 2. RESTFUL_API_DOCUMENTATION.md
**完整 REST API 文档** - OpenAI 风格专业文档
```
内容:
✓ 所有 API 端点详细说明
✓ 认证和安全
✓ 请求/响应示例
✓ 错误代码和处理
✓ 限流规则
✓ 最佳实践
✓ 性能优化建议
✓ 多语言代码示例
适合:
• API 集成开发者
• 需要完整技术参考
• 生产环境部署
```
**章节导航**:
- [Introduction](#introduction) - API 介绍
- [Authentication](#authentication) - 认证方式
- [API Endpoints](#api-endpoints) - 所有端点
- [Error Handling](#error-handling) - 错误处理
- [Rate Limiting](#rate-limiting) - 限流规则
- [Best Practices](#best-practices) - 最佳实践
---
### 3. API_CLIENT_EXAMPLES.md
**多语言客户端示例** - 复制粘贴即可用
```
内容:
✓ Python基础 + 高级)
✓ JavaScript/Node.js
✓ PHP
✓ Go
✓ Java
✓ cURL + Bash 脚本
✓ 完整客户端类实现
✓ 批量处理示例
✓ 错误处理和重试
适合:
• 需要快速集成的开发者
• 学习如何调用 API
• 参考最佳实践
```
**每种语言包含**:
- 基础示例(最简单用法)
- 高级示例(错误处理、重试、批量)
- 完整客户端类
- 生产级代码
---
### 4. API_SERVICE_GUIDE.md
**商业化部署完整方案** - MVP 到规模化
```
内容:
✓ MVP 最小可行产品设计
✓ 商业化架构单机→K8s
✓ 成本分析(详细预算)
✓ 收益模型(定价建议)
✓ 部署方案对比
✓ 实施路线图
✓ 技术栈选择
✓ 监控和告警
适合:
• 创业者/产品经理
• 技术负责人
• 架构师
• CTO
```
**核心章节**:
- MVP 阶段1-2个月
- 产品优化2-4个月
- 规模化4-6个月
- Docker vs Kubernetes 对比
- 成本与扩展性分析
---
### 5. openapi.yaml
**OpenAPI 3.0.3 规范** - 机器可读的 API 定义
```
用途:
✓ Swagger UI 自动渲染
✓ Redoc 文档生成
✓ 客户端代码生成
✓ API 测试工具
✓ Mock 服务器
工具支持:
• Swagger UI
• Redoc
• Postman
• Insomnia
• OpenAPI Generator
• Swagger Codegen
```
**在线查看**:
```bash
# 启动服务后访问
http://localhost:8080/docs # Swagger UI
http://localhost:8080/redoc # ReDoc
```
**验证规范**:
```bash
# 安装 OpenAPI 验证工具
npm install -g @apidevtools/swagger-cli
# 验证文件
swagger-cli validate openapi.yaml
```
---
### 6. IOPaint_API.postman_collection.json
**Postman Collection V2.1** - API 测试集合
```
包含:
✓ 所有 API 端点
✓ 预配置的测试脚本
✓ 环境变量模板
✓ 示例请求/响应
✓ 自动化测试
功能:
• 一键导入
• 快速测试
• 自动化测试
• 团队分享
```
**导入步骤**:
1. 打开 Postman
2. File → Import
3. 选择 `IOPaint_API.postman_collection.json`
4. 设置变量:
- `base_url`: `http://localhost:8080`
- `api_key`: `your_secret_key_change_me`
5. 开始测试
---
## 🔗 相关资源
### 在线资源
- **项目仓库**: https://github.com/let5sne/IOPaint
- **API 分支**: https://github.com/let5sne/IOPaint/tree/feature/api-service
- **在线文档**: http://localhost:8080/docs (需先启动服务)
### 开发工具
| 工具 | 用途 | 链接 |
|------|------|------|
| Postman | API 测试 | https://www.postman.com/ |
| Swagger UI | API 文档 | https://swagger.io/tools/swagger-ui/ |
| Redoc | API 文档 | https://github.com/Redocly/redoc |
| OpenAPI Generator | 代码生成 | https://openapi-generator.tech/ |
### 推荐阅读
- [RESTful API 设计指南](https://github.com/microsoft/api-guidelines)
- [OpenAPI 规范](https://swagger.io/specification/)
- [FastAPI 文档](https://fastapi.tiangolo.com/)
- [Docker 最佳实践](https://docs.docker.com/develop/dev-best-practices/)
---
## 💡 使用建议
### 新手路径
```
1. 阅读 API_SERVICE_README.md了解基础
2. 启动服务并访问 /docs在线测试
3. 导入 Postman Collection实际测试
4. 参考 API_CLIENT_EXAMPLES.md集成到应用
```
### 开发者路径
```
1. 阅读 RESTFUL_API_DOCUMENTATION.md理解 API
2. 使用 openapi.yaml 生成客户端代码
3. 参考 Best Practices 优化集成
4. 阅读 API_SERVICE_GUIDE.md了解架构
```
### 决策者路径
```
1. 阅读 API_SERVICE_GUIDE.md了解方案
2. 评估成本和收益模型
3. 选择合适的部署方案
4. 制定实施路线图
```
---
## 📞 获取帮助
### 问题排查
1. **API 无法访问** → 查看 [API_SERVICE_README.md - 故障排查](./API_SERVICE_README.md#故障排查)
2. **认证失败** → 查看 [RESTFUL_API_DOCUMENTATION.md - Authentication](./RESTFUL_API_DOCUMENTATION.md#authentication)
3. **性能问题** → 查看 [RESTFUL_API_DOCUMENTATION.md - Best Practices](./RESTFUL_API_DOCUMENTATION.md#best-practices)
4. **部署问题** → 查看 [API_SERVICE_GUIDE.md - 部署方案](./API_SERVICE_GUIDE.md#部署方案对比)
### 联系方式
- **GitHub Issues**: https://github.com/let5sne/IOPaint/issues
- **文档反馈**: 在对应文档提 Issue
---
## 📝 文档更新日志
### 2025-11-28
**新增**:
- ✨ API_SERVICE_README.md - 快速开始指南
- ✨ RESTFUL_API_DOCUMENTATION.md - 完整 REST API 文档
- ✨ API_CLIENT_EXAMPLES.md - 多语言客户端示例
- ✨ API_SERVICE_GUIDE.md - 商业化部署方案
- ✨ openapi.yaml - OpenAPI 3.0.3 规范
- ✨ IOPaint_API.postman_collection.json - Postman 测试集合
- ✨ API_DOCS_INDEX.md - 文档导航(本文档)
**特点**:
- 符合 OpenAPI 标准
- OpenAI 风格专业文档
- 多语言代码示例
- 完整的部署方案
- 生产级最佳实践
---
**快速开始**: 阅读 [API_SERVICE_README.md](./API_SERVICE_README.md) 👈
**完整 API 参考**: 阅读 [RESTFUL_API_DOCUMENTATION.md](./RESTFUL_API_DOCUMENTATION.md) 👈
**商业化方案**: 阅读 [API_SERVICE_GUIDE.md](./API_SERVICE_GUIDE.md) 👈

526
docs/API_SERVICE_GUIDE.md Normal file
View File

@@ -0,0 +1,526 @@
# IOPaint 去水印 API 服务设计方案
## 📋 目录
1. [MVP 最小可行产品](#mvp-最小可行产品)
2. [商业化架构设计](#商业化架构设计)
3. [部署方案对比](#部署方案对比)
4. [成本与扩展性分析](#成本与扩展性分析)
---
## 🚀 MVP 最小可行产品
### 设计原则KISS
- **单一功能**只提供去水印API不包含WebUI
- **单一模型**只使用LaMa模型快速、低资源
- **简单认证**API Key认证
- **本地存储**:无需对象存储
- **单机部署**Docker Compose即可
### 核心改造
#### 1. 精简API服务 (`api_service.py`)
```python
# 只保留核心功能:
# - POST /api/v1/remove-watermark - 去水印接口
# - GET /api/v1/health - 健康检查
# - GET /api/v1/usage - 使用统计(可选)
# 移除功能:
# - WebUI相关路由
# - 多模型支持
# - 插件系统
# - 文件浏览器
# - Socket.IO实时通信
```
#### 2. API接口设计
**去水印接口**
```bash
POST /api/v1/remove-watermark
Headers:
X-API-Key: your_api_key_here
Content-Type: multipart/form-data
Body:
image: file (必需) - 原始图片
mask: file (可选) - 水印遮罩,不提供则自动检测
Response:
- 200: 返回处理后的图片image/png
- 401: API Key无效
- 400: 参数错误
- 500: 处理失败
```
**健康检查**
```bash
GET /api/v1/health
Response: {"status": "ok", "model": "lama"}
```
#### 3. MVP部署架构
```
┌─────────────────────────────────────┐
│ Nginx (反向代理) │
│ - SSL终止 │
│ - 限流 (rate limiting) │
│ - 日志记录 │
└──────────────┬──────────────────────┘
┌──────────────▼──────────────────────┐
│ IOPaint API Service │
│ - FastAPI │
│ - LaMa模型 │
│ - API Key认证 │
│ - 本地存储 │
└─────────────────────────────────────┘
```
#### 4. MVP Docker配置
**单容器方案**:适合月处理量 < 10万张
```yaml
# docker-compose.mvp.yml
version: '3.8'
services:
api:
build:
context: .
dockerfile: docker/APIDockerfile
ports:
- "8080:8080"
environment:
- API_KEY=your_secret_key_here
- MAX_IMAGE_SIZE=4096
- ENABLE_METRICS=true
volumes:
- ./models:/root/.cache
- ./logs:/app/logs
restart: unless-stopped
deploy:
resources:
limits:
cpus: '2'
memory: 4G
```
**成本估算MVP阶段**
- **云服务器**2核4G约¥200-300/月(阿里云、腾讯云)
- **存储**100GB SSD约¥50/月
- **流量**100GB/月约¥50/月
- **总计**约¥300-400/月
**性能预估**
- 处理速度约1-2秒/张1024x1024
- 并发能力2-4个请求
- 月处理量:~5-10万张
---
## 🏢 商业化架构设计
### 设计原则
- **横向扩展**:支持动态增减实例
- **高可用**:无单点故障
- **异步处理**:支持批量和队列
- **监控完善**:实时监控和告警
- **成本优化**:按需扩展
### 商业化架构图
```
┌─────────────────┐
│ CDN / CloudFlare │
└────────┬────────┘
┌────────▼────────┐
│ Load Balancer │ (Nginx/HAProxy/ALB)
│ - SSL终止 │
│ - 限流 │
│ - WAF │
└────────┬────────┘
┌───────────────────┼───────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│API Pod 1│ │API Pod 2│ │API Pod N│
│ (GPU) │ │ (GPU) │ │ (GPU) │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└───────────────────┼───────────────────┘
┌───────────────────┼───────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ Redis │ │PostgreSQL│ │ S3 │
│ (队列) │ │ (元数据) │ │ (存储) │
└─────────┘ └──────────┘ └─────────┘
┌────▼────┐
│ Celery │
│ Worker │
└─────────┘
┌────▼────┐
│Prometheus│
│ Grafana │
└─────────┘
```
### 核心组件
#### 1. API层Kubernetes部署
**api-deployment.yaml**
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: iopaint-api
spec:
replicas: 3 # 根据负载自动扩展
template:
spec:
containers:
- name: api
image: let5sne/iopaint-api:latest
resources:
requests:
memory: "4Gi"
cpu: "2"
nvidia.com/gpu: 1
limits:
memory: "8Gi"
cpu: "4"
nvidia.com/gpu: 1
env:
- name: REDIS_URL
value: "redis://redis:6379"
- name: S3_BUCKET
value: "iopaint-images"
```
#### 2. 异步任务队列Redis + Celery
**好处**
- 避免API超时
- 支持批量处理
- 可重试失败任务
- 平滑处理流量峰值
**工作流程**
```
1. 用户上传图片 → API返回任务ID
2. 图片存入S3 → 任务推入Redis队列
3. Celery Worker异步处理
4. 处理完成 → 更新数据库 → 触发回调/Webhook
```
#### 3. 数据库设计PostgreSQL
```sql
-- 用户表
CREATE TABLE users (
id SERIAL PRIMARY KEY,
api_key VARCHAR(64) UNIQUE NOT NULL,
plan VARCHAR(20) NOT NULL, -- free, basic, pro, enterprise
quota_monthly INT NOT NULL,
quota_used INT DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW()
);
-- 任务表
CREATE TABLE tasks (
id UUID PRIMARY KEY,
user_id INT REFERENCES users(id),
status VARCHAR(20) NOT NULL, -- pending, processing, completed, failed
image_url TEXT NOT NULL,
result_url TEXT,
created_at TIMESTAMP DEFAULT NOW(),
completed_at TIMESTAMP,
processing_time_ms INT
);
-- 使用统计表(按日汇总)
CREATE TABLE usage_stats (
date DATE NOT NULL,
user_id INT REFERENCES users(id),
requests_count INT DEFAULT 0,
success_count INT DEFAULT 0,
avg_processing_time_ms INT,
PRIMARY KEY (date, user_id)
);
```
#### 4. 监控与告警
**Prometheus指标**
```python
# 核心业务指标
requests_total = Counter('api_requests_total', 'Total API requests', ['status', 'endpoint'])
processing_time = Histogram('image_processing_seconds', 'Image processing time')
model_inference_time = Histogram('model_inference_seconds', 'Model inference time')
queue_size = Gauge('redis_queue_size', 'Current queue size')
gpu_utilization = Gauge('gpu_utilization', 'GPU utilization %')
```
**告警规则**
- API错误率 > 5%
- 队列积压 > 1000
- GPU利用率 > 90%持续5分钟
- 响应时间 > 10秒P95
#### 5. 成本优化策略
**弹性伸缩**
```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: iopaint-api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: iopaint-api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: nvidia.com/gpu
target:
type: Utilization
averageUtilization: 80
```
**Spot实例**
- 使用云厂商Spot/抢占式实例成本降低60-80%
- 配合优先级队列,重要任务用按需实例
---
## 🔄 部署方案对比
| 方案 | 适用场景 | 优点 | 缺点 | 月成本估算 |
|------|---------|------|------|-----------|
| **Docker单机** | 个人/小团队<br>月< 10万张 | • 部署简单<br>• 成本低<br>• 维护容易 | • 无法扩展<br>• 单点故障<br>• 性能有限 | ¥300-500 |
| **Docker Compose多容器** | 小型商业<br>月10-50万张 | • 支持多实例<br>• 负载均衡<br>• 成本可控 | • 手动扩展<br>• 监控有限<br>• 高可用差 | ¥1000-3000 |
| **Kubernetes** | 中大型商业<br>月50万张+ | • 自动扩展<br>• 高可用<br>• 完善监控<br>• 多云部署 | • 复杂度高<br>• 学习成本<br>• 初期成本高 | ¥5000-20000+ |
| **Serverless (Lambda/云函数)** | 不规则流量<br>峰谷明显 | • 按用付费<br>• 无需运维<br>• 无限扩展 | • 冷启动慢<br>• GPU支持差<br>• 单次限制 | 按用量计费 |
---
## 💰 成本与扩展性分析
### MVP阶段月处理10万张
**方案单机Docker**
```
硬件:
- 云服务器 2核4GCPU版本¥200/月
- GPU服务器 4核16G + T4GPU版本¥800/月
存储:
- 系统盘 100GB SSD¥50/月
- 模型缓存:~5GBLaMa
带宽:
- 假设平均每张图500KB10万张 = 50GB
- 上传 + 下载 = 100GB约¥60/月
总计:
- CPU版本约¥310/月
- GPU版本约¥910/月推荐处理速度快10倍
```
### 商业化阶段月处理100万张
**方案Kubernetes + GPU节点池**
```
计算资源3个GPU节点自动扩展
- 3 x (4核16G + T4 GPU)¥2400/月
- 高峰期额外2个节点Spot实例¥400/月
数据库:
- PostgreSQL云数据库2核4G¥300/月
- Redis云实例2G¥150/月
存储:
- 对象存储 500GB¥100/月
- 数据库存储 100GB¥50/月
CDN + 流量:
- CDN加速¥200/月
- 带宽流量1TB¥600/月
监控 + 日志:
- 日志服务¥100/月
- 监控告警¥100/月
负载均衡¥100/月
总计约¥4500-5000/月
```
**收益模型(参考)**
```
定价方案:
- Free: 10张/天,免费
- Basic: ¥99/月3000张
- Pro: ¥399/月20000张
- Enterprise: ¥1999/月150000张优先处理
假设用户分布:
- Free用户1000人 = 0元引流
- Basic用户200人 = ¥19,800
- Pro用户50人 = ¥19,950
- Enterprise10人 = ¥19,990
月收入约¥59,740
月成本约¥5,000
月利润约¥54,740
```
---
## 📝 推荐实施路线
### 阶段1MVP验证1-2个月
**目标**验证市场需求获取前100个付费用户
**技术栈**
- Docker单机部署
- FastAPI + LaMa模型
- 简单API Key认证
- SQLite本地数据库
**投入**
- 开发时间1周
- 服务器成本¥300-500/月
- 域名+SSL¥100/年
**里程碑**
- [ ] API服务上线
- [ ] 文档和示例代码
- [ ] 支付集成(微信/支付宝)
- [ ] 获取前10个付费用户
- [ ] 收集用户反馈
### 阶段2产品优化2-4个月
**目标**优化体验扩展到1000付费用户
**技术栈**
- Docker Compose多容器
- PostgreSQL数据库
- Redis缓存
- 简单监控Prometheus
**投入**
- 开发时间2周
- 服务器成本¥1000-2000/月
**里程碑**
- [ ] 批量处理API
- [ ] Webhook回调
- [ ] 使用Dashboard
- [ ] 自动检测水印(可选)
- [ ] API SDKPython/Node.js
### 阶段3规模化4-6个月
**目标**:支持月百万级处理,稳定盈利
**技术栈**
- Kubernetes集群
- 对象存储
- 完整监控体系
- 多模型支持(可选)
**投入**
- 开发时间4周
- 基础设施成本¥5000-10000/月
**里程碑**
- [ ] 自动扩展
- [ ] 多区域部署
- [ ] SLA保证99.9%
- [ ] 企业级支持
---
## 🎯 关键建议
### 1. MVP阶段重点
**做**
- 专注核心功能(去水印)
- 简单可靠的API
- 完善的文档和示例
- 快速迭代
**不做**
- 复杂的功能(多模型、插件)
- 过度设计的架构
- 过早优化性能
- WebUI界面
### 2. Docker vs Kubernetes
**用Docker如果**
- 月处理量 < 50万张
- 团队 < 3人
- 预算有限
- 流量相对稳定
**用Kubernetes如果**
- 月处理量 > 50万张
- 需要高可用99.9%+
- 流量波动大
- 计划多区域部署
### 3. 技术债务控制
**从一开始就做好**
- API版本控制/api/v1/
- 完善的错误处理和日志
- API限流和认证
- 数据备份策略
**可以后续优化**
- 监控系统(先简单后完善)
- 自动扩展(先手动后自动)
- 多模型支持(先单模型验证)
- 高级功能(批量、回调等)
### 4. 安全建议
**必须**
- HTTPS强制
- API Key认证
- 请求限流
- 输入验证(文件大小、格式)
- 敏感信息加密
**推荐**
- WAF防护
- DDoS防护
- 审计日志
- 定期安全扫描
---
## 📚 参考资源
- [FastAPI最佳实践](https://fastapi.tiangolo.com/tutorial/)
- [Kubernetes生产实践](https://kubernetes.io/docs/setup/production-environment/)
- [AWS架构最佳实践](https://aws.amazon.com/architecture/well-architected/)
- [API设计指南](https://github.com/microsoft/api-guidelines)

303
docs/API_SERVICE_README.md Normal file
View File

@@ -0,0 +1,303 @@
# IOPaint 去水印 API 服务
专注于提供去水印功能的精简API服务适合商业化部署。
## 🎯 项目特点
- **单一职责**专注去水印功能移除WebUI和其他复杂功能
- **高性能**使用LaMa模型1-2秒处理一张1024x1024图片
- **易部署**Docker一键部署支持CPU和GPU
- **低成本**MVP阶段月成本约¥300-500
- **可扩展**:提供完整的商业化架构方案
## 📚 文档
- [完整设计方案](./API_SERVICE_GUIDE.md) - MVP到商业化的完整路线图
- [客户端示例](./API_CLIENT_EXAMPLES.md) - Python、JavaScript、cURL等多语言调用示例
## 🚀 快速开始
### 方式1Docker Compose部署推荐
```bash
# 1. 设置API密钥
export API_KEY="your_secret_key_here"
# 2. 启动服务GPU版本
docker-compose -f docker-compose.mvp.yml up -d
# 3. 检查服务状态
curl http://localhost:8080/api/v1/health
# 4. 测试去水印
curl -X POST http://localhost:8080/api/v1/remove-watermark \
-H "X-API-Key: $API_KEY" \
-F "image=@test.jpg" \
-o result.png
```
### 方式2直接运行Python脚本
```bash
# 1. 安装依赖
pip3 install -r requirements.txt
pip3 install -e .
# 2. 设置环境变量
export API_KEY="your_secret_key_here"
# 3. 启动服务
python3 api_service_mvp.py
# 4. 访问 http://localhost:8080/docs 查看API文档
```
## 🔧 配置说明
### 环境变量
| 变量名 | 说明 | 默认值 |
|--------|------|--------|
| `API_KEY` | API访问密钥 | `your_secret_key_change_me` |
| `MAX_IMAGE_SIZE` | 最大图片边长(像素) | `4096` |
| `ENABLE_METRICS` | 启用统计指标 | `true` |
### 硬件要求
**最低配置CPU版本**
- CPU: 2核
- 内存: 4GB
- 磁盘: 20GB
- 性能: ~10-15秒/张
**推荐配置GPU版本**
- CPU: 4核
- 内存: 8GB
- GPU: NVIDIA T4或更好2GB+ VRAM
- 磁盘: 30GB
- 性能: ~1-2秒/张
## 📖 API文档
### 核心接口
#### 1. 去水印接口
```http
POST /api/v1/remove-watermark
```
**请求头**
- `X-API-Key`: API密钥必需
- `Content-Type`: multipart/form-data
**请求体**
- `image`: 图片文件(必需)
- `mask`: 遮罩图片(可选,白色区域将被修复)
**响应**
- 成功返回处理后的PNG图片
- 失败返回JSON错误信息
**示例**
```bash
curl -X POST http://localhost:8080/api/v1/remove-watermark \
-H "X-API-Key: your_key" \
-F "image=@input.jpg" \
-o result.png
```
#### 2. 健康检查
```http
GET /api/v1/health
```
**响应**
```json
{
"status": "healthy",
"model": "lama",
"device": "cuda",
"gpu_available": true
}
```
#### 3. 使用统计
```http
GET /api/v1/stats
```
**请求头**
- `X-API-Key`: API密钥必需
**响应**
```json
{
"total": 1000,
"success": 980,
"failed": 20,
"avg_processing_time": 1.5
}
```
## 💡 使用示例
### Python
```python
import requests
def remove_watermark(image_path, api_key):
url = "http://localhost:8080/api/v1/remove-watermark"
headers = {"X-API-Key": api_key}
files = {"image": open(image_path, "rb")}
response = requests.post(url, headers=headers, files=files)
if response.status_code == 200:
with open("result.png", "wb") as f:
f.write(response.content)
print("✓ 处理成功!")
else:
print(f"✗ 失败: {response.json()}")
remove_watermark("test.jpg", "your_api_key")
```
更多语言示例请查看 [API_CLIENT_EXAMPLES.md](./API_CLIENT_EXAMPLES.md)
## 📊 性能基准
基于NVIDIA T4 GPU测试
| 图片尺寸 | 处理时间 | 内存占用 | 每秒处理 |
|----------|---------|----------|---------|
| 512x512 | ~0.8秒 | ~1.5GB | ~1.25张/秒 |
| 1024x1024 | ~1.5秒 | ~2GB | ~0.67张/秒 |
| 2048x2048 | ~4秒 | ~3.5GB | ~0.25张/秒 |
| 4096x4096 | ~15秒 | ~6GB | ~0.07张/秒 |
## 🏗️ 架构方案
### MVP阶段月处理10万张
- **部署方式**Docker单机
- **成本**约¥300-500/月
- **支持用户**100-500人
### 商业化阶段月处理100万张
- **部署方式**Kubernetes + GPU节点池
- **成本**约¥5000-10000/月
- **支持用户**5000+人
- **特性**
- 自动扩展
- 异步队列Redis + Celery
- 对象存储S3/OSS
- 完整监控Prometheus + Grafana
详细架构请查看 [API_SERVICE_GUIDE.md](./API_SERVICE_GUIDE.md)
## 💰 定价建议(参考)
| 套餐 | 价格 | 额度 | 适用场景 |
|------|------|------|---------|
| **Free** | ¥0/月 | 10张/天 | 个人测试 |
| **Basic** | ¥99/月 | 3000张 | 小型工作室 |
| **Pro** | ¥399/月 | 20000张 | 中型企业 |
| **Enterprise** | ¥1999/月 | 150000张 | 大型企业 |
## 🔒 安全建议
1. **生产环境务必修改默认API密钥**
2. **使用HTTPS**配置Nginx SSL
3. **启用限流**(防止滥用)
4. **定期备份数据库**
5. **监控异常访问**
## 🐛 故障排查
### 常见问题
**1. API返回401错误**
- 检查X-API-Key header是否正确
- 确认API_KEY环境变量已设置
**2. 处理速度慢**
- CPU模式考虑升级到GPU
- GPU模式检查显存是否充足
- 检查图片是否过大
**3. Docker容器无法启动**
- GPU版本确认nvidia-docker已安装
- 检查端口8080是否被占用
- 查看日志:`docker-compose logs api`
**4. 返回500错误**
- 查看服务日志:`tail -f logs/api_*.log`
- 检查磁盘空间是否充足
- 确认模型文件已下载
## 📈 监控指标
推荐监控以下指标:
- **业务指标**
- 请求总数
- 成功率
- 平均处理时间
- 队列长度
- **系统指标**
- CPU使用率
- GPU使用率
- 内存使用
- 磁盘I/O
- **告警阈值**
- 错误率 > 5%
- 响应时间P95 > 10秒
- GPU利用率 > 90%持续5分钟
## 🚦 实施路线图
### 第1周MVP上线
- [ ] 部署API服务
- [ ] 编写使用文档
- [ ] 集成支付系统
- [ ] 获取前10个用户反馈
### 第2-4周产品优化
- [ ] 优化处理速度
- [ ] 添加批量处理API
- [ ] 实现Webhook回调
- [ ] 创建使用Dashboard
### 第2-3个月规模化
- [ ] 迁移到Kubernetes
- [ ] 添加自动扩展
- [ ] 实现异步队列
- [ ] 完善监控系统
## 📞 技术支持
- **文档**: [API_SERVICE_GUIDE.md](./API_SERVICE_GUIDE.md)
- **示例**: [API_CLIENT_EXAMPLES.md](./API_CLIENT_EXAMPLES.md)
- **问题反馈**: https://github.com/let5sne/IOPaint/issues
- **在线文档**: `http://localhost:8080/docs` (Swagger UI)
## 📄 许可证
本项目基于 Apache-2.0 许可证开源。
---
**⚡ 立即开始:**
```bash
git clone https://github.com/let5sne/IOPaint.git
cd IOPaint
export API_KEY="your_secret_key"
docker-compose -f docker-compose.mvp.yml up -d
```
访问 http://localhost:8080/docs 查看完整API文档

373
docs/BRANCH_README.md Normal file
View File

@@ -0,0 +1,373 @@
# IOPaint API Service Branch
这是 **IOPaint 去水印 API 服务** 的独立分支,专注于提供商业化的 REST API 服务。
## 🌿 分支说明
### 主要分支对比
| 分支 | 用途 | 特点 | 适用场景 |
|------|------|------|---------|
| **main/master** | 完整版 IOPaint | • WebUI 界面<br>• 多种模型<br>• 插件系统<br>• 批处理功能 | 个人使用、本地工具 |
| **feature/api-service** | API 服务版 | • 纯 REST API<br>• 单一模型LaMa<br>• 商业化就绪<br>• 易于部署 | 企业集成、SaaS 服务 |
## 📦 本分支内容
### 核心文件
**服务代码**:
- `api_service_mvp.py` - 精简的 API 服务实现
- `docker/APIDockerfile` - API 服务 Docker 镜像
- `docker-compose.mvp.yml` - MVP 部署配置
- `nginx/nginx.conf` - Nginx 反向代理配置
**完整文档**:
- `API_DOCS_INDEX.md` - 📑 文档导航(从这里开始)
- `RESTFUL_API_DOCUMENTATION.md` - 📖 完整 REST API 文档
- `API_SERVICE_README.md` - 🚀 快速开始指南
- `API_CLIENT_EXAMPLES.md` - 💻 多语言客户端示例
- `API_SERVICE_GUIDE.md` - 🏗️ 商业化部署方案
- `openapi.yaml` - 🔧 OpenAPI 3.0.3 规范
- `IOPaint_API.postman_collection.json` - 🧪 Postman 测试集合
### 与主分支的差异
**移除的功能**:
- ❌ WebUI 界面(前端代码仍在,但不使用)
- ❌ 多模型支持(只保留 LaMa
- ❌ 插件系统
- ❌ 文件浏览器
- ❌ Socket.IO 实时通信
**新增的功能**:
- ✅ RESTful API 服务
- ✅ API Key 认证
- ✅ 使用统计
- ✅ 完整的 API 文档
- ✅ 多语言客户端示例
- ✅ 商业化部署方案
## 🚀 快速开始
### 1. 克隆并切换到 API 分支
```bash
# 克隆仓库
git clone https://github.com/let5sne/IOPaint.git
cd IOPaint
# 切换到 API 服务分支
git checkout feature/api-service
# 查看分支
git branch
# * feature/api-service
# main
# master
```
### 2. 启动服务
```bash
# 设置 API 密钥
export API_KEY="your_secret_key_here"
# 启动服务GPU 版本)
docker-compose -f docker-compose.mvp.yml up -d
# 或者直接运行 Python需要先安装依赖
python3 api_service_mvp.py
```
### 3. 测试 API
```bash
# 健康检查
curl http://localhost:8080/api/v1/health
# 去水印
curl -X POST http://localhost:8080/api/v1/remove-watermark \
-H "X-API-Key: $API_KEY" \
-F "image=@test.jpg" \
-o result.png
```
### 4. 查看在线文档
访问 http://localhost:8080/docsSwagger UI
## 📚 文档导航
**新手必读**:
1. [📑 文档总览](./API_DOCS_INDEX.md) - 从这里开始
2. [🚀 快速开始](./API_SERVICE_README.md) - 10分钟上手
**开发者集成**:
1. [📖 REST API 完整文档](./RESTFUL_API_DOCUMENTATION.md)
2. [💻 多语言客户端示例](./API_CLIENT_EXAMPLES.md)
3. [🔧 OpenAPI 规范](./openapi.yaml)
**商业化部署**:
1. [🏗️ 完整部署方案](./API_SERVICE_GUIDE.md)
2. [💰 成本与收益分析](./API_SERVICE_GUIDE.md#成本与扩展性分析)
## 🔄 分支切换指南
### 切换到主分支WebUI 版本)
```bash
# 切换到 main 分支
git checkout main
# 启动 WebUI 版本
python3 main.py start --model lama --device cuda --port 8080
```
### 切换回 API 分支
```bash
# 切换到 API 服务分支
git checkout feature/api-service
# 启动 API 服务
docker-compose -f docker-compose.mvp.yml up -d
```
### 保持两个版本同时运行
```bash
# 方法1使用不同端口
# 主分支WebUI使用 8080
git checkout main
python3 main.py start --model lama --port 8080
# API 分支使用 8081
git checkout feature/api-service
docker-compose -f docker-compose.mvp.yml up -d
# 修改 docker-compose.mvp.yml 中的端口为 8081
# 方法2使用不同目录
mkdir -p ~/iopaint-webui ~/iopaint-api
git clone https://github.com/let5sne/IOPaint.git ~/iopaint-webui
git clone https://github.com/let5sne/IOPaint.git ~/iopaint-api
cd ~/iopaint-webui && git checkout main
cd ~/iopaint-api && git checkout feature/api-service
```
## 🎯 使用场景
### 使用主分支main如果你需要
- ✅ 本地使用图形界面
- ✅ 尝试不同的 AI 模型
- ✅ 使用插件RemoveBG、RealESRGAN 等)
- ✅ 批处理本地图片
- ✅ 个人/团队内部工具
### 使用 API 分支feature/api-service如果你需要
- ✅ 集成到自己的应用
- ✅ 提供在线服务
- ✅ 商业化部署
- ✅ 自动化处理
- ✅ 远程调用 API
## 📊 性能对比
| 项目 | 主分支 | API 分支 |
|------|--------|----------|
| **启动时间** | ~30秒 | ~10秒 |
| **内存占用** | ~3-4GB | ~2-3GB |
| **镜像大小** | ~8GB | ~6GB |
| **API 响应** | 需要 WebUI | 原生 REST API |
| **并发支持** | 有限 | 良好(可扩展)|
| **部署难度** | 简单 | 中等(但文档齐全)|
## 🔐 安全建议
### API 分支特有的安全考虑
1. **API Key 管理**
```bash
# 生产环境必须修改默认密钥
export API_KEY=$(openssl rand -hex 32)
```
2. **HTTPS 强制**
```bash
# 使用 Nginx 配置 SSL
# 参考 nginx/nginx.conf
```
3. **限流保护**
```nginx
# Nginx 已配置限流
# 每秒 10 个请求,突发 20 个
```
4. **日志监控**
```bash
# 查看日志
tail -f logs/api_*.log
```
## 🛠️ 维护指南
### 更新依赖
```bash
# 切换到 API 分支
git checkout feature/api-service
# 更新 Python 依赖
pip install -r requirements.txt --upgrade
# 重建 Docker 镜像
docker-compose -f docker-compose.mvp.yml build --no-cache
```
### 同步主分支的修复
```bash
# 如果主分支有重要修复,可以选择性合并
git checkout feature/api-service
# 只合并特定文件
git checkout main -- iopaint/model/
git checkout main -- iopaint/helper.py
# 提交
git commit -m "sync: 同步主分支的模型修复"
```
### 版本标签
```bash
# 创建版本标签
git tag -a api-v1.0.0 -m "API Service v1.0.0 - MVP Release"
git push origin api-v1.0.0
# 查看所有 API 版本
git tag -l "api-v*"
```
## 📈 发展路线图
### 当前版本v1.0.0
- ✅ 基础 REST API
- ✅ LaMa 模型支持
- ✅ API Key 认证
- ✅ 完整文档
- ✅ Docker 部署
### 计划中v1.1.0
- 🔜 批量处理 API
- 🔜 Webhook 回调
- 🔜 自动检测水印
- 🔜 使用 Dashboard
### 未来版本v2.0.0
- 🔮 多模型支持SD、SDXL
- 🔮 异步处理队列
- 🔮 对象存储集成
- 🔮 Kubernetes Helm Chart
## 🤝 贡献指南
### API 分支的贡献
如果你想为 API 服务分支做贡献:
```bash
# 1. Fork 仓库
# 2. 创建功能分支
git checkout -b feature/api-new-feature feature/api-service
# 3. 开发并测试
# 4. 提交 Pull Request 到 feature/api-service
```
### 文档改进
```bash
# 改进文档
git checkout -b docs/improve-api-docs feature/api-service
# 编辑文档
vim RESTFUL_API_DOCUMENTATION.md
# 提交
git commit -m "docs: 改进 API 认证说明"
git push origin docs/improve-api-docs
```
## 📞 获取帮助
### API 分支特定问题
- **GitHub Issues**: https://github.com/let5sne/IOPaint/issues
- 标签:`api-service`, `documentation`, `deployment`
- **分支地址**: https://github.com/let5sne/IOPaint/tree/feature/api-service
### 常见问题
**Q: 为什么要独立分支?**
A: 主分支专注于 WebUI 体验API 分支专注于服务化和商业化,两者目标不同。
**Q: API 分支会合并到主分支吗?**
A: 不会。两个分支将独立发展,但会同步重要的 bug 修复。
**Q: 如何选择使用哪个分支?**
A: 个人使用选主分支WebUI企业集成选 API 分支。
**Q: API 分支可以使用其他模型吗?**
A: 当前只支持 LaMa未来版本会添加更多模型。
**Q: API 分支支持批量处理吗?**
A: v1.0 不支持v1.1 计划添加批量 API。
## 📄 许可证
本项目(包括 API 服务分支)基于 Apache-2.0 许可证开源。
## 🎉 总结
### API 服务分支的优势
- ✅ **专注**:只做 API 服务,代码更精简
- ✅ **高效**:启动快,资源占用少
- ✅ **专业**:完整的商业化文档和部署方案
- ✅ **灵活**:易于集成到任何应用
- ✅ **可靠**:生产就绪,经过优化
### 立即开始
```bash
# 1. 克隆并切换分支
git clone https://github.com/let5sne/IOPaint.git
cd IOPaint
git checkout feature/api-service
# 2. 阅读文档
cat API_DOCS_INDEX.md
# 3. 启动服务
export API_KEY="your_secret_key"
docker-compose -f docker-compose.mvp.yml up -d
# 4. 访问文档
open http://localhost:8080/docs
```
---
**分支**: `feature/api-service`
**版本**: v1.0.0
**更新**: 2025-11-28
**维护者**: [@let5sne](https://github.com/let5sne)
**快速链接**:
- [📑 文档导航](./API_DOCS_INDEX.md)
- [🚀 快速开始](./API_SERVICE_README.md)
- [📖 完整 API 文档](./RESTFUL_API_DOCUMENTATION.md)
- [🏗️ 部署方案](./API_SERVICE_GUIDE.md)

View File

@@ -0,0 +1,461 @@
{
"info": {
"name": "IOPaint Watermark Removal API",
"description": "AI-powered watermark removal service using LaMa model.\n\n## Quick Start\n\n1. Set your API key in the collection variables\n2. Import sample images for testing\n3. Run requests\n\n## Authentication\n\nAll endpoints (except health check) require an API key in the `X-API-Key` header.\n\nGet your API key from: https://iopaint.com/dashboard",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"version": "1.0.0"
},
"auth": {
"type": "apikey",
"apikey": [
{
"key": "value",
"value": "{{api_key}}",
"type": "string"
},
{
"key": "key",
"value": "X-API-Key",
"type": "string"
}
]
},
"variable": [
{
"key": "base_url",
"value": "http://localhost:8080",
"type": "string"
},
{
"key": "api_key",
"value": "your_secret_key_change_me",
"type": "string"
}
],
"item": [
{
"name": "Processing",
"item": [
{
"name": "Remove Watermark",
"event": [
{
"listen": "test",
"script": {
"exec": [
"// Test response status",
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"// Test content type",
"pm.test(\"Content-Type is image/png\", function () {",
" pm.response.to.have.header(\"Content-Type\", \"image/png\");",
"});",
"",
"// Test processing time header exists",
"pm.test(\"Has X-Processing-Time header\", function () {",
" pm.response.to.have.header(\"X-Processing-Time\");",
"});",
"",
"// Test image size header exists",
"pm.test(\"Has X-Image-Size header\", function () {",
" pm.response.to.have.header(\"X-Image-Size\");",
"});",
"",
"// Log processing time",
"const processingTime = pm.response.headers.get(\"X-Processing-Time\");",
"const imageSize = pm.response.headers.get(\"X-Image-Size\");",
"console.log(`Processing time: ${processingTime}s`);",
"console.log(`Image size: ${imageSize}`);"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "formdata",
"formdata": [
{
"key": "image",
"description": "Image file to process (JPEG, PNG, WebP)",
"type": "file",
"src": []
},
{
"key": "mask",
"description": "Optional mask (white = remove, black = keep)",
"type": "file",
"src": [],
"disabled": true
}
]
},
"url": {
"raw": "{{base_url}}/api/v1/remove-watermark",
"host": ["{{base_url}}"],
"path": ["api", "v1", "remove-watermark"]
},
"description": "Remove watermarks or unwanted objects from images.\n\n### Request\n- **image** (required): Image file to process\n- **mask** (optional): Mask image (white areas will be removed)\n\n### Response\n- Returns processed image as PNG\n- Headers include processing time and image size\n\n### Example\n```bash\ncurl -X POST http://localhost:8080/api/v1/remove-watermark \\\n -H \"X-API-Key: your_key\" \\\n -F \"image=@test.jpg\" \\\n -o result.png\n```"
},
"response": [
{
"name": "Success - Image Processed",
"originalRequest": {
"method": "POST",
"header": [
{
"key": "X-API-Key",
"value": "{{api_key}}"
}
],
"body": {
"mode": "formdata",
"formdata": [
{
"key": "image",
"type": "file",
"src": "/path/to/image.jpg"
}
]
},
"url": {
"raw": "{{base_url}}/api/v1/remove-watermark",
"host": ["{{base_url}}"],
"path": ["api", "v1", "remove-watermark"]
}
},
"status": "OK",
"code": 200,
"_postman_previewlanguage": "png",
"header": [
{
"key": "Content-Type",
"value": "image/png"
},
{
"key": "X-Processing-Time",
"value": "1.52"
},
{
"key": "X-Image-Size",
"value": "1024x768"
}
],
"body": "<binary PNG data>"
},
{
"name": "Error - Invalid API Key",
"originalRequest": {
"method": "POST",
"header": [
{
"key": "X-API-Key",
"value": "invalid_key"
}
],
"body": {
"mode": "formdata",
"formdata": [
{
"key": "image",
"type": "file",
"src": []
}
]
},
"url": {
"raw": "{{base_url}}/api/v1/remove-watermark",
"host": ["{{base_url}}"],
"path": ["api", "v1", "remove-watermark"]
}
},
"status": "Unauthorized",
"code": 401,
"_postman_previewlanguage": "json",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": "{\n \"error\": \"Unauthorized\",\n \"detail\": \"Invalid API Key\",\n \"status_code\": 401\n}"
},
{
"name": "Error - Image Too Large",
"originalRequest": {
"method": "POST",
"header": [
{
"key": "X-API-Key",
"value": "{{api_key}}"
}
],
"body": {
"mode": "formdata",
"formdata": [
{
"key": "image",
"type": "file",
"src": []
}
]
},
"url": {
"raw": "{{base_url}}/api/v1/remove-watermark",
"host": ["{{base_url}}"],
"path": ["api", "v1", "remove-watermark"]
}
},
"status": "Bad Request",
"code": 400,
"_postman_previewlanguage": "json",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": "{\n \"error\": \"Bad Request\",\n \"detail\": \"Image too large. Max dimension: 4096px\",\n \"status_code\": 400\n}"
}
]
},
{
"name": "Remove Watermark (With Mask)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"Content-Type is image/png\", function () {",
" pm.response.to.have.header(\"Content-Type\", \"image/png\");",
"});"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "formdata",
"formdata": [
{
"key": "image",
"description": "Original image",
"type": "file",
"src": []
},
{
"key": "mask",
"description": "Mask image (white = remove, black = keep)",
"type": "file",
"src": []
}
]
},
"url": {
"raw": "{{base_url}}/api/v1/remove-watermark",
"host": ["{{base_url}}"],
"path": ["api", "v1", "remove-watermark"]
},
"description": "Remove watermarks with a custom mask for precise control.\n\n### Mask Guidelines\n- **White (255)**: Areas to remove/inpaint\n- **Black (0)**: Areas to preserve\n- **Gray**: Partial inpainting (blend)\n\n### Creating Masks\n- Use any image editor (Photoshop, GIMP, etc.)\n- Paint white over watermark areas\n- Save as PNG\n- Mask will be auto-resized to match image dimensions"
},
"response": []
}
],
"description": "Image processing endpoints"
},
{
"name": "Monitoring",
"item": [
{
"name": "Health Check",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"Response has status field\", function () {",
" var jsonData = pm.response.json();",
" pm.expect(jsonData).to.have.property('status');",
"});",
"",
"pm.test(\"Service is healthy\", function () {",
" var jsonData = pm.response.json();",
" pm.expect(jsonData.status).to.eql('healthy');",
"});",
"",
"// Log service info",
"var jsonData = pm.response.json();",
"console.log(`Model: ${jsonData.model}`);",
"console.log(`Device: ${jsonData.device}`);",
"console.log(`GPU Available: ${jsonData.gpu_available}`);"
],
"type": "text/javascript"
}
}
],
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/health",
"host": ["{{base_url}}"],
"path": ["api", "v1", "health"]
},
"description": "Check API service status and availability.\n\n### No Authentication Required\n\n### Response Fields\n- **status**: Service status (`healthy` or `unhealthy`)\n- **model**: Current AI model name\n- **device**: Compute device (`cuda` or `cpu`)\n- **gpu_available**: GPU availability\n\n### Example\n```bash\ncurl http://localhost:8080/api/v1/health\n```"
},
"response": [
{
"name": "Success - Service Healthy",
"originalRequest": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/health",
"host": ["{{base_url}}"],
"path": ["api", "v1", "health"]
}
},
"status": "OK",
"code": 200,
"_postman_previewlanguage": "json",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": "{\n \"status\": \"healthy\",\n \"model\": \"lama\",\n \"device\": \"cuda\",\n \"gpu_available\": true\n}"
}
]
}
],
"description": "Service monitoring and health checks",
"auth": {
"type": "noauth"
}
},
{
"name": "Account",
"item": [
{
"name": "Get Usage Statistics",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"Response has required fields\", function () {",
" var jsonData = pm.response.json();",
" pm.expect(jsonData).to.have.property('total');",
" pm.expect(jsonData).to.have.property('success');",
" pm.expect(jsonData).to.have.property('failed');",
" pm.expect(jsonData).to.have.property('avg_processing_time');",
"});",
"",
"// Calculate and log success rate",
"var jsonData = pm.response.json();",
"if (jsonData.total > 0) {",
" var successRate = (jsonData.success / jsonData.total * 100).toFixed(2);",
" console.log(`Success Rate: ${successRate}%`);",
" console.log(`Average Processing Time: ${jsonData.avg_processing_time}s`);",
" console.log(`Total Requests: ${jsonData.total}`);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/stats",
"host": ["{{base_url}}"],
"path": ["api", "v1", "stats"]
},
"description": "Get account usage statistics.\n\n### Authentication Required\nRequires `X-API-Key` header.\n\n### Response Fields\n- **total**: Total requests made\n- **success**: Successful requests\n- **failed**: Failed requests\n- **total_processing_time**: Cumulative processing time (seconds)\n- **avg_processing_time**: Average processing time (seconds)\n\n### Example\n```bash\ncurl http://localhost:8080/api/v1/stats \\\n -H \"X-API-Key: your_key\"\n```"
},
"response": [
{
"name": "Success - Usage Stats",
"originalRequest": {
"method": "GET",
"header": [
{
"key": "X-API-Key",
"value": "{{api_key}}"
}
],
"url": {
"raw": "{{base_url}}/api/v1/stats",
"host": ["{{base_url}}"],
"path": ["api", "v1", "stats"]
}
},
"status": "OK",
"code": 200,
"_postman_previewlanguage": "json",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": "{\n \"total\": 1250,\n \"success\": 1230,\n \"failed\": 20,\n \"total_processing_time\": 1845.5,\n \"avg_processing_time\": 1.5\n}"
},
{
"name": "Error - Metrics Disabled",
"originalRequest": {
"method": "GET",
"header": [
{
"key": "X-API-Key",
"value": "{{api_key}}"
}
],
"url": {
"raw": "{{base_url}}/api/v1/stats",
"host": ["{{base_url}}"],
"path": ["api", "v1", "stats"]
}
},
"status": "Not Found",
"code": 404,
"_postman_previewlanguage": "json",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": "{\n \"error\": \"Not Found\",\n \"detail\": \"Metrics disabled\",\n \"status_code\": 404\n}"
}
]
}
],
"description": "Account and usage information"
}
]
}

77
docs/README.md Normal file
View File

@@ -0,0 +1,77 @@
# IOPaint API Service Documentation
Welcome to the IOPaint API Service documentation! This directory contains all the documentation for the API service branch.
## 📑 Documentation Index
### Quick Start
- **[API Service README](./API_SERVICE_README.md)** - 🚀 Get started in 10 minutes
- **[Documentation Index](./API_DOCS_INDEX.md)** - 📑 Complete navigation guide
### API Reference
- **[RESTful API Documentation](./RESTFUL_API_DOCUMENTATION.md)** - 📖 Complete API reference (OpenAI style)
- **[OpenAPI Specification](./openapi.yaml)** - 🔧 OpenAPI 3.0.3 spec file
- **[Postman Collection](./IOPaint_API.postman_collection.json)** - 🧪 API testing collection
### Integration & Examples
- **[Client Examples](./API_CLIENT_EXAMPLES.md)** - 💻 Multi-language code examples
- Python (Basic + Advanced)
- JavaScript/Node.js
- PHP
- Go
- Java
- cURL/Bash
### Deployment & Architecture
- **[Service Guide](./API_SERVICE_GUIDE.md)** - 🏗️ MVP to production deployment guide
- Architecture design (Docker → Kubernetes)
- Cost analysis & revenue models
- Implementation roadmap
### Branch Information
- **[Branch README](./BRANCH_README.md)** - 🌿 Branch comparison and switching guide
- **[Upgrade Notes](./UPGRADE_NOTES.md)** - 📝 Package upgrade history
## 🎯 Choose Your Path
### I want to...
**...get started quickly**
→ Read [API_SERVICE_README.md](./API_SERVICE_README.md)
**...integrate the API**
→ Read [RESTFUL_API_DOCUMENTATION.md](./RESTFUL_API_DOCUMENTATION.md) + [API_CLIENT_EXAMPLES.md](./API_CLIENT_EXAMPLES.md)
**...test the API**
→ Import [IOPaint_API.postman_collection.json](./IOPaint_API.postman_collection.json) or visit http://localhost:8080/docs
**...deploy for production**
→ Read [API_SERVICE_GUIDE.md](./API_SERVICE_GUIDE.md)
**...understand branch differences**
→ Read [BRANCH_README.md](./BRANCH_README.md)
## 📊 Documentation Stats
| Type | Files | Pages | Languages |
|------|-------|-------|-----------|
| Core Docs | 6 | ~120 | - |
| Code Examples | 1 | ~28 | 6 |
| Spec Files | 2 | - | - |
| **Total** | **9** | **~150** | **6** |
## 🔗 External Links
- **Repository**: https://github.com/let5sne/IOPaint
- **API Branch**: https://github.com/let5sne/IOPaint/tree/feature/api-service
- **Main Branch**: https://github.com/let5sne/IOPaint/tree/main
## 📞 Support
- **GitHub Issues**: https://github.com/let5sne/IOPaint/issues
- **Documentation Feedback**: Open an issue with label `documentation`
---
**Last Updated**: 2025-11-28
**Version**: 1.0.0

View File

@@ -0,0 +1,992 @@
# IOPaint REST API Documentation
Official REST API documentation for IOPaint Watermark Removal Service.
**Version:** v1
**Base URL:** `https://api.iopaint.com` (production) or `http://localhost:8080` (development)
**Protocol:** HTTPS (production), HTTP (development)
---
## Table of Contents
1. [Introduction](#introduction)
2. [Authentication](#authentication)
3. [API Endpoints](#api-endpoints)
4. [Models](#models)
5. [Error Handling](#error-handling)
6. [Rate Limiting](#rate-limiting)
7. [Best Practices](#best-practices)
8. [SDKs & Libraries](#sdks--libraries)
9. [Changelog](#changelog)
---
## Introduction
The IOPaint API provides AI-powered watermark removal capabilities through a simple REST API. Built on the LaMa (Large Mask Inpainting) model, it offers fast and high-quality image restoration.
### Key Features
- **Fast Processing**: 1-2 seconds for 1024x1024 images
- **High Quality**: State-of-the-art AI model
- **Simple Integration**: Standard REST API with multipart/form-data
- **Flexible**: Optional mask support for precise control
- **Scalable**: From hobby projects to enterprise deployments
### API Capabilities
| Feature | Status | Description |
|---------|--------|-------------|
| Watermark Removal | ✅ Available | Remove watermarks from images |
| Auto Detection | ⏳ Coming Soon | Automatic watermark detection |
| Batch Processing | ⏳ Coming Soon | Process multiple images at once |
| Webhook Callbacks | ⏳ Coming Soon | Async processing with callbacks |
---
## Authentication
The IOPaint API uses API keys for authentication. All requests must include your API key in the request header.
### Obtaining an API Key
1. Sign up at [https://iopaint.com/signup](https://iopaint.com/signup)
2. Navigate to your [Dashboard](https://iopaint.com/dashboard)
3. Generate a new API key
4. Store it securely (keys cannot be recovered)
### Using Your API Key
Include your API key in the `X-API-Key` header with every request:
```http
X-API-Key: your_api_key_here
```
### Security Best Practices
- ⚠️ **Never expose your API key in client-side code**
- ✅ Store keys in environment variables
- ✅ Rotate keys periodically
- ✅ Use different keys for development and production
- ✅ Revoke compromised keys immediately
### Example
```bash
# ✅ Correct
curl https://api.iopaint.com/api/v1/health \
-H "X-API-Key: $IOPAINT_API_KEY"
# ❌ Incorrect (hardcoded key)
curl https://api.iopaint.com/api/v1/health \
-H "X-API-Key: sk_live_1234567890abcdef"
```
---
## API Endpoints
### Base URL
```
Production: https://api.iopaint.com
Development: http://localhost:8080
```
### Endpoint Overview
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/v1/remove-watermark` | POST | Remove watermark from image |
| `/api/v1/health` | GET | Service health check |
| `/api/v1/stats` | GET | Account usage statistics |
---
## 1. Remove Watermark
Remove watermarks or unwanted objects from images using AI.
### Endpoint
```http
POST /api/v1/remove-watermark
```
### Authentication
Required. Include `X-API-Key` header.
### Request Headers
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| `X-API-Key` | string | Yes | Your API key |
| `Content-Type` | string | Yes | Must be `multipart/form-data` |
### Request Body
Submit as `multipart/form-data`:
| Field | Type | Required | Max Size | Description |
|-------|------|----------|----------|-------------|
| `image` | file | Yes | 10 MB | Image to process (JPEG, PNG, WebP) |
| `mask` | file | No | 10 MB | Optional mask (white = remove, black = keep) |
### Image Requirements
- **Formats**: JPEG, PNG, WebP
- **Max Dimension**: 4096px (width or height)
- **Max File Size**: 10 MB
- **Color Mode**: RGB (grayscale will be converted)
### Mask Requirements
- **Format**: PNG (8-bit grayscale or RGB)
- **Dimension**: Must match image size (auto-resized if different)
- **White (255)**: Areas to remove/inpaint
- **Black (0)**: Areas to preserve
- **Gray**: Partial inpainting (blend)
### Response
**Success (200 OK)**
Returns the processed image as `image/png`.
**Response Headers:**
| Header | Type | Description |
|--------|------|-------------|
| `Content-Type` | string | `image/png` |
| `X-Processing-Time` | float | Processing time in seconds |
| `X-Image-Size` | string | Original image dimensions (e.g., "1024x768") |
### Examples
#### cURL
```bash
# Basic usage (no mask)
curl -X POST https://api.iopaint.com/api/v1/remove-watermark \
-H "X-API-Key: $IOPAINT_API_KEY" \
-F "image=@/path/to/image.jpg" \
-o result.png
# With mask
curl -X POST https://api.iopaint.com/api/v1/remove-watermark \
-H "X-API-Key: $IOPAINT_API_KEY" \
-F "image=@/path/to/image.jpg" \
-F "mask=@/path/to/mask.png" \
-o result.png
# Verbose output
curl -X POST https://api.iopaint.com/api/v1/remove-watermark \
-H "X-API-Key: $IOPAINT_API_KEY" \
-F "image=@image.jpg" \
-v \
-o result.png
```
#### Python
```python
import requests
url = "https://api.iopaint.com/api/v1/remove-watermark"
headers = {"X-API-Key": "your_api_key_here"}
# Basic usage
with open("image.jpg", "rb") as f:
files = {"image": f}
response = requests.post(url, headers=headers, files=files)
if response.status_code == 200:
with open("result.png", "wb") as f:
f.write(response.content)
print(f"✓ Success! Processing time: {response.headers['X-Processing-Time']}s")
else:
print(f"✗ Error {response.status_code}: {response.json()}")
```
#### JavaScript/Node.js
```javascript
const FormData = require('form-data');
const fs = require('fs');
const axios = require('axios');
const form = new FormData();
form.append('image', fs.createReadStream('image.jpg'));
axios.post('https://api.iopaint.com/api/v1/remove-watermark', form, {
headers: {
'X-API-Key': process.env.IOPAINT_API_KEY,
...form.getHeaders()
},
responseType: 'arraybuffer'
})
.then(response => {
fs.writeFileSync('result.png', response.data);
console.log('✓ Success!');
})
.catch(error => {
console.error('✗ Error:', error.response?.status, error.response?.data);
});
```
#### PHP
```php
<?php
$ch = curl_init('https://api.iopaint.com/api/v1/remove-watermark');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'image' => new CURLFile('/path/to/image.jpg')
]);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: your_api_key_here'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode == 200) {
file_put_contents('result.png', $result);
echo "✓ Success!\n";
} else {
echo "✗ Error: $httpCode\n";
}
?>
```
#### Go
```go
package main
import (
"bytes"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
// Add image file
file, _ := os.Open("image.jpg")
defer file.Close()
part, _ := writer.CreateFormFile("image", "image.jpg")
io.Copy(part, file)
writer.Close()
// Create request
req, _ := http.NewRequest("POST", "https://api.iopaint.com/api/v1/remove-watermark", body)
req.Header.Set("X-API-Key", os.Getenv("IOPAINT_API_KEY"))
req.Header.Set("Content-Type", writer.FormDataContentType())
// Send request
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
// Save result
out, _ := os.Create("result.png")
defer out.Close()
io.Copy(out, resp.Body)
}
```
---
## 2. Health Check
Check the API service status and availability.
### Endpoint
```http
GET /api/v1/health
```
### Authentication
Not required.
### Response
**Success (200 OK)**
```json
{
"status": "healthy",
"model": "lama",
"device": "cuda",
"gpu_available": true
}
```
**Response Fields:**
| Field | Type | Description |
|-------|------|-------------|
| `status` | string | Service status: `healthy` or `unhealthy` |
| `model` | string | Current AI model name |
| `device` | string | Compute device: `cuda` (GPU) or `cpu` |
| `gpu_available` | boolean | GPU availability |
### Example
```bash
curl https://api.iopaint.com/api/v1/health
```
```python
import requests
response = requests.get("https://api.iopaint.com/api/v1/health")
print(response.json())
# Output: {'status': 'healthy', 'model': 'lama', 'device': 'cuda', 'gpu_available': True}
```
---
## 3. Usage Statistics
Retrieve your account usage statistics.
### Endpoint
```http
GET /api/v1/stats
```
### Authentication
Required. Include `X-API-Key` header.
### Response
**Success (200 OK)**
```json
{
"total": 1250,
"success": 1230,
"failed": 20,
"total_processing_time": 1845.5,
"avg_processing_time": 1.5
}
```
**Response Fields:**
| Field | Type | Description |
|-------|------|-------------|
| `total` | integer | Total requests made |
| `success` | integer | Successful requests |
| `failed` | integer | Failed requests |
| `total_processing_time` | float | Cumulative processing time (seconds) |
| `avg_processing_time` | float | Average processing time (seconds) |
### Example
```bash
curl https://api.iopaint.com/api/v1/stats \
-H "X-API-Key: $IOPAINT_API_KEY"
```
```python
import requests
headers = {"X-API-Key": "your_api_key_here"}
response = requests.get("https://api.iopaint.com/api/v1/stats", headers=headers)
stats = response.json()
print(f"Total requests: {stats['total']}")
print(f"Success rate: {stats['success'] / stats['total'] * 100:.2f}%")
print(f"Average time: {stats['avg_processing_time']:.2f}s")
```
---
## Models
### LaMa (Current)
**Large Mask Inpainting** - Fast and efficient inpainting model.
| Property | Value |
|----------|-------|
| **Name** | `lama` |
| **Speed** | ⚡⚡⚡⚡⚡ (1-2s for 1024x1024) |
| **Quality** | ⭐⭐⭐⭐ |
| **VRAM** | ~1GB |
| **Best For** | Watermark removal, object removal |
### Future Models (Coming Soon)
| Model | Speed | Quality | VRAM | Use Case |
|-------|-------|---------|------|----------|
| **SD Inpainting** | ⚡⚡⚡ | ⭐⭐⭐⭐⭐ | ~4GB | Creative editing |
| **SDXL Inpainting** | ⚡⚡ | ⭐⭐⭐⭐⭐ | ~8GB | Professional work |
---
## Error Handling
### Error Response Format
All errors return a JSON object with the following structure:
```json
{
"error": "ErrorType",
"detail": "Human-readable error message",
"status_code": 400
}
```
### HTTP Status Codes
| Code | Status | Description |
|------|--------|-------------|
| `200` | OK | Request successful |
| `400` | Bad Request | Invalid request parameters |
| `401` | Unauthorized | Missing or invalid API key |
| `413` | Payload Too Large | File exceeds size limit |
| `429` | Too Many Requests | Rate limit exceeded |
| `500` | Internal Server Error | Server-side error |
| `503` | Service Unavailable | Service temporarily unavailable |
### Error Types
#### 401 Unauthorized
**Missing API Key:**
```json
{
"error": "Unauthorized",
"detail": "Missing API Key. Please provide X-API-Key header.",
"status_code": 401
}
```
**Invalid API Key:**
```json
{
"error": "Unauthorized",
"detail": "Invalid API Key",
"status_code": 401
}
```
#### 400 Bad Request
**Invalid Image Format:**
```json
{
"error": "Bad Request",
"detail": "Invalid image format: cannot identify image file",
"status_code": 400
}
```
**Image Too Large:**
```json
{
"error": "Bad Request",
"detail": "Image too large. Max dimension: 4096px",
"status_code": 400
}
```
**File Too Large:**
```json
{
"error": "Bad Request",
"detail": "Image too large. Max size: 10MB",
"status_code": 400
}
```
#### 429 Rate Limit Exceeded
```json
{
"error": "Too Many Requests",
"detail": "Rate limit exceeded. Please try again later.",
"status_code": 429
}
```
**Headers:**
```http
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1672531200
```
#### 500 Internal Server Error
```json
{
"error": "Internal Server Error",
"detail": "Processing failed: CUDA out of memory",
"status_code": 500
}
```
### Error Handling Best Practices
```python
import requests
import time
def remove_watermark_with_retry(image_path, max_retries=3):
"""Remove watermark with exponential backoff retry"""
url = "https://api.iopaint.com/api/v1/remove-watermark"
headers = {"X-API-Key": os.getenv("IOPAINT_API_KEY")}
for attempt in range(max_retries):
try:
with open(image_path, "rb") as f:
response = requests.post(
url,
headers=headers,
files={"image": f},
timeout=120
)
if response.status_code == 200:
return response.content
elif response.status_code == 429:
# Rate limit - exponential backoff
wait_time = 2 ** attempt
print(f"Rate limited. Waiting {wait_time}s...")
time.sleep(wait_time)
continue
elif response.status_code in [500, 503]:
# Server error - retry
if attempt < max_retries - 1:
time.sleep(2 ** attempt)
continue
else:
raise Exception(f"Server error: {response.json()}")
else:
# Client error - don't retry
raise Exception(f"Error {response.status_code}: {response.json()}")
except requests.Timeout:
if attempt < max_retries - 1:
print(f"Timeout. Retrying... ({attempt + 1}/{max_retries})")
continue
else:
raise
raise Exception("Max retries exceeded")
```
---
## Rate Limiting
### Rate Limit Rules
| Plan | Rate Limit | Burst | Quota |
|------|------------|-------|-------|
| **Free** | 2 req/min | 5 | 10/day |
| **Basic** | 10 req/min | 20 | 3,000/month |
| **Pro** | 30 req/min | 60 | 20,000/month |
| **Enterprise** | Custom | Custom | Custom |
### Rate Limit Headers
Every response includes rate limit information:
```http
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 7
X-RateLimit-Reset: 1672531200
```
| Header | Description |
|--------|-------------|
| `X-RateLimit-Limit` | Maximum requests per time window |
| `X-RateLimit-Remaining` | Remaining requests in current window |
| `X-RateLimit-Reset` | Unix timestamp when limit resets |
### Handling Rate Limits
```python
import requests
import time
def wait_for_rate_limit_reset(response):
"""Wait until rate limit resets"""
if response.status_code == 429:
reset_time = int(response.headers.get('X-RateLimit-Reset', 0))
current_time = int(time.time())
wait_time = max(0, reset_time - current_time)
print(f"Rate limited. Waiting {wait_time}s for reset...")
time.sleep(wait_time + 1) # Add 1s buffer
return True
return False
```
### Best Practices
1. **Check remaining quota** before making requests
2. **Implement exponential backoff** for retries
3. **Cache results** to avoid duplicate requests
4. **Use batch endpoints** (when available) for multiple images
5. **Upgrade your plan** if consistently hitting limits
---
## Best Practices
### Performance Optimization
#### 1. Image Preprocessing
```python
from PIL import Image
def optimize_image(image_path, max_size=2048):
"""Resize large images before upload"""
img = Image.open(image_path)
# Check if resize needed
if max(img.size) > max_size:
ratio = max_size / max(img.size)
new_size = tuple(int(dim * ratio) for dim in img.size)
img = img.resize(new_size, Image.LANCZOS)
# Convert to RGB if needed
if img.mode != 'RGB':
img = img.convert('RGB')
# Save optimized
output = "optimized.jpg"
img.save(output, quality=95, optimize=True)
return output
```
#### 2. Concurrent Processing
```python
from concurrent.futures import ThreadPoolExecutor
import requests
def process_batch(image_paths, api_key, max_workers=4):
"""Process multiple images concurrently"""
def process_one(path):
headers = {"X-API-Key": api_key}
with open(path, "rb") as f:
response = requests.post(
"https://api.iopaint.com/api/v1/remove-watermark",
headers=headers,
files={"image": f}
)
return path, response
with ThreadPoolExecutor(max_workers=max_workers) as executor:
results = list(executor.map(process_one, image_paths))
return results
```
#### 3. Connection Reuse
```python
import requests
# Reuse session for multiple requests
session = requests.Session()
session.headers.update({"X-API-Key": api_key})
for image_path in image_paths:
with open(image_path, "rb") as f:
response = session.post(
"https://api.iopaint.com/api/v1/remove-watermark",
files={"image": f}
)
```
### Security
#### Environment Variables
```bash
# .env file
IOPAINT_API_KEY=sk_live_1234567890abcdef
# .gitignore
.env
```
```python
import os
from dotenv import load_dotenv
load_dotenv()
api_key = os.getenv("IOPAINT_API_KEY")
```
#### API Key Rotation
```python
# Rotate keys periodically
def rotate_api_key():
old_key = os.getenv("IOPAINT_API_KEY")
new_key = generate_new_key() # From dashboard
# Test new key
test_response = requests.get(
"https://api.iopaint.com/api/v1/health",
headers={"X-API-Key": new_key}
)
if test_response.status_code == 200:
# Update environment
update_env("IOPAINT_API_KEY", new_key)
# Revoke old key from dashboard
revoke_key(old_key)
```
### Cost Optimization
#### 1. Cache Results
```python
import hashlib
import os
def get_cached_result(image_path):
"""Check if result already cached"""
# Generate cache key from file content
with open(image_path, "rb") as f:
file_hash = hashlib.md5(f.read()).hexdigest()
cache_path = f"cache/{file_hash}.png"
if os.path.exists(cache_path):
return cache_path
return None
def process_with_cache(image_path, api_key):
"""Process with caching"""
# Check cache first
cached = get_cached_result(image_path)
if cached:
print(f"✓ Using cached result: {cached}")
return cached
# Process via API
result = remove_watermark(image_path, api_key)
# Save to cache
with open(image_path, "rb") as f:
file_hash = hashlib.md5(f.read()).hexdigest()
cache_path = f"cache/{file_hash}.png"
with open(cache_path, "wb") as f:
f.write(result)
return cache_path
```
#### 2. Monitor Usage
```python
def check_quota_before_request(api_key):
"""Check quota before making expensive requests"""
headers = {"X-API-Key": api_key}
response = requests.get(
"https://api.iopaint.com/api/v1/stats",
headers=headers
)
stats = response.json()
quota_used = stats['total']
plan_limit = 3000 # Basic plan
remaining = plan_limit - quota_used
if remaining < 100:
print(f"⚠️ Warning: Only {remaining} requests remaining!")
return remaining > 0
```
---
## SDKs & Libraries
### Official SDKs
#### Python
```bash
pip install iopaint-sdk
```
```python
from iopaint import IOPaintClient
client = IOPaintClient(api_key="your_api_key")
# Simple usage
result = client.remove_watermark("image.jpg")
# With options
result = client.remove_watermark(
image="image.jpg",
mask="mask.png",
output="result.png"
)
# Batch processing
results = client.batch_process(
images=["img1.jpg", "img2.jpg"],
output_dir="./results"
)
```
#### JavaScript/TypeScript
```bash
npm install iopaint-sdk
```
```javascript
const { IOPaintClient } = require('iopaint-sdk');
const client = new IOPaintClient({
apiKey: process.env.IOPAINT_API_KEY
});
// Simple usage
await client.removeWatermark('image.jpg', 'result.png');
// With mask
await client.removeWatermark('image.jpg', 'result.png', {
mask: 'mask.png'
});
```
### Community Libraries
| Language | Library | Author |
|----------|---------|--------|
| Go | `iopaint-go` | Community |
| Ruby | `iopaint-rb` | Community |
| Java | `iopaint-java` | Community |
| PHP | `iopaint-php` | Community |
---
## Changelog
### v1.0.0 (2025-11-28)
**Initial Release**
-`/api/v1/remove-watermark` endpoint
-`/api/v1/health` endpoint
-`/api/v1/stats` endpoint
- ✨ API key authentication
- ✨ Rate limiting
- ✨ Support for JPEG, PNG, WebP
- ✨ Optional mask support
- ✨ Processing time metrics
### Coming Soon
- 🔜 `/api/v1/batch` - Batch processing endpoint
- 🔜 `/api/v1/detect` - Automatic watermark detection
- 🔜 Webhook callbacks for async processing
- 🔜 Additional models (SD, SDXL)
- 🔜 Custom model training
---
## Support
### Resources
- **API Status**: [status.iopaint.com](https://status.iopaint.com)
- **Dashboard**: [iopaint.com/dashboard](https://iopaint.com/dashboard)
- **Documentation**: [docs.iopaint.com](https://docs.iopaint.com)
- **GitHub**: [github.com/let5sne/IOPaint](https://github.com/let5sne/IOPaint)
### Contact
- **Email**: support@iopaint.com
- **Discord**: [discord.gg/iopaint](https://discord.gg/iopaint)
- **Issues**: [GitHub Issues](https://github.com/let5sne/IOPaint/issues)
### SLA (Service Level Agreement)
| Plan | Uptime | Support Response |
|------|--------|------------------|
| Free | 95% | Community |
| Basic | 99% | 48 hours |
| Pro | 99.5% | 24 hours |
| Enterprise | 99.9% | 4 hours |
---
## Legal
### Terms of Service
By using the IOPaint API, you agree to our [Terms of Service](https://iopaint.com/terms).
### Privacy Policy
We respect your privacy. See our [Privacy Policy](https://iopaint.com/privacy).
### Data Processing
- Images are processed in memory and not stored
- Uploaded images are deleted immediately after processing
- We do not train models on your data
- Logs contain only metadata (no image content)
### Usage Restrictions
**Allowed:**
- ✅ Removing watermarks from your own content
- ✅ Restoring old photos
- ✅ Object removal for creative projects
- ✅ Commercial use (with appropriate plan)
**Prohibited:**
- ❌ Removing copyright protection
- ❌ Processing illegal content
- ❌ Violating third-party rights
- ❌ Automated scraping without permission
---
**© 2025 IOPaint. All rights reserved.**
*Last updated: 2025-11-28*

177
docs/UPGRADE_NOTES.md Normal file
View File

@@ -0,0 +1,177 @@
# IOPaint 依赖包升级说明
## 升级日期
2025-11-28
## 升级概述
本次升级将项目的主要依赖包更新到了最新的稳定版本,以获得更好的性能、更多功能和安全性改进。
## 包版本变化
### 核心 AI 库
| 包名 | 原版本 | 新版本 | 说明 |
|------|--------|--------|------|
| diffusers | 0.27.2 | ≥0.35.0 | Hugging Face 扩散模型库,支持更多新模型 |
| huggingface_hub | 0.25.2 | ≥0.26.0 | 模型下载和管理 |
| peft | 0.7.1 | ≥0.13.0 | 参数高效微调库 |
| transformers | ≥4.39.1 | ≥4.45.0 | Transformer 模型库 |
| controlnet-aux | 0.0.3 | ≥0.0.9 | ControlNet 预处理工具 |
### Web 框架
| 包名 | 原版本 | 新版本 | 说明 |
|------|--------|--------|------|
| fastapi | 0.108.0 | ≥0.115.0 | Web API 框架 |
| gradio | 4.21.0 | ≥5.0.0,<6.0.0 | Web UI 框架(限制<6.0以避免破坏性变更) |
| python-socketio | 5.7.2 | ≥5.11.0 | WebSocket 支持 |
### 工具库
| 包名 | 原版本 | 新版本 | 说明 |
|------|--------|--------|------|
| Pillow | 9.5.0 | ≥10.0.0 | 图像处理库 |
| piexif | 1.1.3 | ≥1.1.3 | EXIF 处理 |
| typer-config | 1.4.0 | ≥1.4.0 | CLI 配置 |
## 代码修改
### 1. 修复 PyTorch 弃用警告
**文件:** `iopaint/model/ldm.py:279`
**修改前:**
```python
@torch.cuda.amp.autocast()
def forward(self, image, mask, config: InpaintRequest):
```
**修改后:**
```python
@torch.amp.autocast('cuda')
def forward(self, image, mask, config: InpaintRequest):
```
**原因:** PyTorch 2.x 更新了 autocast API旧版本已被弃用。
## 兼容性测试
**所有测试通过:**
- ✓ 核心模块导入
- ✓ Diffusers API 兼容性
- ✓ Gradio 5.x API 兼容性
- ✓ FastAPI 兼容性
- ✓ CLI 命令正常工作
- ✓ 服务器启动正常
## 安装说明
### 使用国内镜像源(推荐)
```bash
# 使用阿里云镜像源
pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
# 或使用清华镜像源
pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
```
### 使用官方源
```bash
pip3 install -r requirements.txt
```
### 验证安装
```bash
# 测试基础导入
python3 -c "from iopaint import entry_point; print('✓ IOPaint 安装成功')"
# 测试 CLI
python3 main.py --help
# 启动服务器测试
python3 main.py start --model lama --device cpu --port 8080
```
## 潜在影响
### 向后兼容性
- ✅ 所有现有功能保持兼容
- ✅ API 接口无变化
- ✅ 配置文件格式无变化
### 性能改进
- 🚀 Diffusers 0.35.x 提供了更快的推理速度
- 🚀 Gradio 5.x 改进了 UI 响应性能
- 🚀 FastAPI 新版本提升了并发处理能力
### 新功能支持
- ✨ 支持更多最新的 Stable Diffusion 模型
- ✨ ControlNet 预处理支持更多模型
- ✨ Gradio 5.x 提供更好的用户体验
## 已知问题
### 警告信息(可忽略)
运行时可能看到以下警告,不影响功能:
- `controlnet_aux` 关于 mediapipe 的警告(除非使用相关功能)
- `timm` 模块导入路径的 FutureWarning
### 解决方案
这些是依赖包的警告,不影响 IOPaint 核心功能。如需消除警告:
```bash
pip3 install mediapipe # 如果使用 MediaPipe 相关功能
```
## 回滚方案
如果遇到问题需要回滚到旧版本:
```bash
# 恢复旧版本
git checkout <previous_commit>
pip3 install -r requirements.txt --force-reinstall
```
或手动安装旧版本:
```bash
pip3 install diffusers==0.27.2 gradio==4.21.0 fastapi==0.108.0 peft==0.7.1 Pillow==9.5.0
```
## 测试建议
升级后建议进行以下测试:
1. **基础功能测试**
```bash
python3 main.py start --model lama --device cpu
```
2. **Diffusion 模型测试**
```bash
python3 main.py start --model runwayml/stable-diffusion-inpainting --device cuda
```
3. **批处理测试**
```bash
python3 main.py run --model lama --device cpu --image <path> --mask <path> --output <path>
```
4. **插件功能测试**
```bash
python3 main.py start --enable-interactive-seg --enable-remove-bg
```
## 联系与反馈
如果在升级过程中遇到问题,请:
1. 检查本文档的"已知问题"部分
2. 查看 GitHub Issues
3. 提交新的 Issue 并附上错误日志
## 更新日志
- 2025-11-28: 首次发布,更新所有主要依赖到最新稳定版本

496
docs/openapi.yaml Normal file
View File

@@ -0,0 +1,496 @@
openapi: 3.0.3
info:
title: IOPaint Watermark Removal API
version: 1.0.0
description: |
AI-powered watermark removal service using state-of-the-art LaMa model.
## Features
- Fast processing (1-2s for 1024x1024 images)
- High-quality results
- Optional mask support
- Simple REST API
## Authentication
All endpoints (except health check) require an API key passed via `X-API-Key` header.
contact:
name: IOPaint Support
email: support@iopaint.com
url: https://iopaint.com/support
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
servers:
- url: https://api.iopaint.com
description: Production server
- url: http://localhost:8080
description: Development server
tags:
- name: Processing
description: Image processing operations
- name: Monitoring
description: Service monitoring and health checks
- name: Account
description: Account and usage information
paths:
/api/v1/remove-watermark:
post:
tags:
- Processing
summary: Remove watermark from image
description: |
Remove watermarks or unwanted objects from images using AI.
### Processing Details
- Model: LaMa (Large Mask Inpainting)
- Processing time: 1-2 seconds for 1024x1024 images
- Max image size: 4096px (width or height)
- Max file size: 10MB
### Mask Usage
- White (255): Areas to remove
- Black (0): Areas to preserve
- Gray: Partial inpainting
operationId: removeWatermark
security:
- ApiKeyAuth: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required:
- image
properties:
image:
type: string
format: binary
description: Image file to process (JPEG, PNG, WebP)
mask:
type: string
format: binary
description: Optional mask (white = remove, black = keep)
encoding:
image:
contentType: image/jpeg, image/png, image/webp
mask:
contentType: image/png
responses:
'200':
description: Successfully processed image
headers:
X-Processing-Time:
description: Processing time in seconds
schema:
type: number
format: float
example: 1.52
X-Image-Size:
description: Original image dimensions
schema:
type: string
example: "1024x768"
content:
image/png:
schema:
type: string
format: binary
'400':
description: Bad request (invalid image, size exceeded, etc.)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
invalidFormat:
summary: Invalid image format
value:
error: "Bad Request"
detail: "Invalid image format: cannot identify image file"
status_code: 400
tooLarge:
summary: Image too large
value:
error: "Bad Request"
detail: "Image too large. Max dimension: 4096px"
status_code: 400
fileTooLarge:
summary: File size exceeded
value:
error: "Bad Request"
detail: "Image too large. Max size: 10MB"
status_code: 400
'401':
description: Unauthorized (missing or invalid API key)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
missingKey:
summary: Missing API key
value:
error: "Unauthorized"
detail: "Missing API Key. Please provide X-API-Key header."
status_code: 401
invalidKey:
summary: Invalid API key
value:
error: "Unauthorized"
detail: "Invalid API Key"
status_code: 401
'413':
description: Payload too large
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: Rate limit exceeded
headers:
X-RateLimit-Limit:
description: Maximum requests per time window
schema:
type: integer
example: 10
X-RateLimit-Remaining:
description: Remaining requests in current window
schema:
type: integer
example: 0
X-RateLimit-Reset:
description: Unix timestamp when limit resets
schema:
type: integer
format: int64
example: 1672531200
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "Too Many Requests"
detail: "Rate limit exceeded. Please try again later."
status_code: 429
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "Internal Server Error"
detail: "Processing failed: CUDA out of memory"
status_code: 500
'503':
description: Service unavailable
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/v1/health:
get:
tags:
- Monitoring
summary: Health check
description: Check API service status and availability
operationId: healthCheck
security: []
responses:
'200':
description: Service is healthy
content:
application/json:
schema:
$ref: '#/components/schemas/HealthResponse'
example:
status: "healthy"
model: "lama"
device: "cuda"
gpu_available: true
/api/v1/stats:
get:
tags:
- Account
summary: Get usage statistics
description: Retrieve account usage statistics including request counts and processing times
operationId: getStats
security:
- ApiKeyAuth: []
responses:
'200':
description: Usage statistics
content:
application/json:
schema:
$ref: '#/components/schemas/StatsResponse'
example:
total: 1250
success: 1230
failed: 20
total_processing_time: 1845.5
avg_processing_time: 1.5
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Metrics disabled
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "Not Found"
detail: "Metrics disabled"
status_code: 404
components:
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
description: |
API key for authentication. Obtain from your dashboard at https://iopaint.com/dashboard
Example: `X-API-Key: sk_live_1234567890abcdef`
schemas:
Error:
type: object
required:
- error
- detail
- status_code
properties:
error:
type: string
description: Error type
example: "Bad Request"
detail:
type: string
description: Human-readable error message
example: "Invalid image format"
status_code:
type: integer
description: HTTP status code
example: 400
HealthResponse:
type: object
required:
- status
- model
- device
- gpu_available
properties:
status:
type: string
enum: [healthy, unhealthy]
description: Service status
example: "healthy"
model:
type: string
description: Current AI model name
example: "lama"
device:
type: string
enum: [cuda, cpu]
description: Compute device
example: "cuda"
gpu_available:
type: boolean
description: GPU availability
example: true
StatsResponse:
type: object
required:
- total
- success
- failed
- total_processing_time
- avg_processing_time
properties:
total:
type: integer
description: Total requests made
example: 1250
minimum: 0
success:
type: integer
description: Successful requests
example: 1230
minimum: 0
failed:
type: integer
description: Failed requests
example: 20
minimum: 0
total_processing_time:
type: number
format: float
description: Cumulative processing time in seconds
example: 1845.5
minimum: 0
avg_processing_time:
type: number
format: float
description: Average processing time in seconds
example: 1.5
minimum: 0
examples:
SuccessfulProcessing:
summary: Successful watermark removal
description: Image processed successfully with processing time
value:
# Binary PNG data returned
# Headers: X-Processing-Time: 1.52, X-Image-Size: 1024x768
InvalidApiKey:
summary: Invalid API key provided
value:
error: "Unauthorized"
detail: "Invalid API Key"
status_code: 401
RateLimitExceeded:
summary: Rate limit exceeded
value:
error: "Too Many Requests"
detail: "Rate limit exceeded. Please try again later."
status_code: 429
x-rate-limits:
- name: Free Plan
limit: 2
period: minute
burst: 5
quota: 10/day
- name: Basic Plan
limit: 10
period: minute
burst: 20
quota: 3000/month
- name: Pro Plan
limit: 30
period: minute
burst: 60
quota: 20000/month
- name: Enterprise Plan
limit: custom
period: custom
burst: custom
quota: custom
x-code-samples:
- lang: cURL
label: cURL
source: |
curl -X POST https://api.iopaint.com/api/v1/remove-watermark \
-H "X-API-Key: $IOPAINT_API_KEY" \
-F "image=@image.jpg" \
-o result.png
- lang: Python
label: Python
source: |
import requests
url = "https://api.iopaint.com/api/v1/remove-watermark"
headers = {"X-API-Key": "your_api_key"}
with open("image.jpg", "rb") as f:
response = requests.post(url, headers=headers, files={"image": f})
if response.status_code == 200:
with open("result.png", "wb") as f:
f.write(response.content)
- lang: JavaScript
label: JavaScript/Node.js
source: |
const FormData = require('form-data');
const fs = require('fs');
const axios = require('axios');
const form = new FormData();
form.append('image', fs.createReadStream('image.jpg'));
axios.post('https://api.iopaint.com/api/v1/remove-watermark', form, {
headers: {
'X-API-Key': process.env.IOPAINT_API_KEY,
...form.getHeaders()
},
responseType: 'arraybuffer'
})
.then(response => {
fs.writeFileSync('result.png', response.data);
});
- lang: PHP
label: PHP
source: |
<?php
$ch = curl_init('https://api.iopaint.com/api/v1/remove-watermark');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'image' => new CURLFile('image.jpg')
]);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: your_api_key'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
file_put_contents('result.png', $result);
curl_close($ch);
?>
- lang: Go
label: Go
source: |
package main
import (
"bytes"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
file, _ := os.Open("image.jpg")
defer file.Close()
part, _ := writer.CreateFormFile("image", "image.jpg")
io.Copy(part, file)
writer.Close()
req, _ := http.NewRequest("POST", "https://api.iopaint.com/api/v1/remove-watermark", body)
req.Header.Set("X-API-Key", os.Getenv("IOPAINT_API_KEY"))
req.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
out, _ := os.Create("result.png")
defer out.Close()
io.Copy(out, resp.Body)
}