Skip to content

Commit

Permalink
Merge branch 'master'
Browse files Browse the repository at this point in the history
  • Loading branch information
shitiandmw committed Nov 23, 2023
2 parents e1d23a3 + 360cb73 commit f115013
Show file tree
Hide file tree
Showing 411 changed files with 9,095 additions and 36,516 deletions.
53 changes: 47 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,59 @@
sd-pics-hub
sd-pics-hub (圆梦照相馆)
===============


<div style="font-size: 1.5rem;">
<a href="./README.md">English</a> |
<a href="./README_ZH.md">中文</a>
<a href="./README_EN.md">English</a> |
<a href="./README.md">中文</a>
</div>
</br>

这是一个利用stable diffusion生成个人AI写真的应用,你只需要上传一张清晰的自拍照,就可以生成多种风格的AI写真

This is an application that utilizes stable diffusion to create personalized AI portraits. You simply need to upload a clear selfie, and the application will generate AI portraits in various styles.
## 目录结构

## Copyright Information
总体目录结构如下

sd-pics-hub is released under the [Apache License 2.0]() open-source license and is available for free use.
```
├─sd-pics-hub
│ ├─api (数据接口部分,使用Egg.js框架)
│ ├─client 客户端,暂定使用hbuild+vue构建,支持多端发布)
│ ├─doc (文档、资料等)
│ │ ├─database.md (数据结构)
│ ├─worker (应用和stable-diffusion沟通的中间件)
│ ├─docker-compose.yml (用于一键启动docker-compose容器编排配置文件)
│ ├─README.md (项目说明文档)
│ ├─README_ZH.md (项目说明文档-中文)
```
## 功能规划

**1.0 版本**

1. 通过roop处理的写真
2. 通过lora模型处理的写真
3. 通过人为干预精修处理的写真,做高级AI摄影作品

## 目前功能

如下图所示,目前可实现使用stable-diffusion生成各种场景的写真照,用roop二次处理后形成自己的写真照

![demo](./demo.png)

## 用到的技术

+ 前端 `uni`,`vue`,`vuex`,`taiwind`...
+ 后端 `nodejs`,`eggjs`,`socket.io`,`redis`,`mongo`,`cos`...
+ 其他 `stable-diffusion`,`nginx`,`mongodb-express`...

## 启动方式

docker-compose up -d

## 你需要关注或修改的配置
...

## 版权信息

sd-pics-hub 遵循 [Apache License 2.0]() 开源协议发布,并提供免费使用。

Copyright © 2023 by EMart
18 changes: 18 additions & 0 deletions README_EN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
sd-pics-hub
===============


<div style="font-size: 1.5rem;">
<a href="./README_EN.md">English</a> |
<a href="./README.md">中文</a>
</div>
</br>


This is an application that utilizes stable diffusion to create personalized AI portraits. You simply need to upload a clear selfie, and the application will generate AI portraits in various styles.

## Copyright Information

sd-pics-hub is released under the [Apache License 2.0]() open-source license and is available for free use.

Copyright © 2023 by EMart
38 changes: 0 additions & 38 deletions README_ZH.md

This file was deleted.

1 change: 1 addition & 0 deletions api/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ run/
*.un~
typings/
.nyc_output/
/tmp
2 changes: 1 addition & 1 deletion api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ RUN npm i --production --registry=https://registry.npmmirror.com
# 拷贝所有源代码到工作目录
COPY . /app

RUN cd /app/lib/plugins/egg-lu-redis && npm i --registry=https://registry.npmmirror.com
# RUN cd /app/lib/plugins/egg-lu-redis && npm i --registry=https://registry.npmmirror.com


WORKDIR /app
Expand Down
26 changes: 26 additions & 0 deletions api/agent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// agent.js
module.exports = agent => {
// 在这里写你的初始化逻辑

// 也可以通过 messenger 对象发送消息给 App Worker
// 但需要等待 App Worker 启动成功后才能发送,不然很可能丢失
agent.messenger.on('egg-ready', () => {});


/**
* 扩展定时任务类型
* 默认框架提供的定时任务只支持每台机器的单个进程执行和全部进程执行,有些情况下,我们的服务并不是单机部署的,这时候可能有一个集群的某一个进程执行一个定时任务的需求。
* 框架并没有直接提供此功能,但开发者可以在上层框架自行扩展新的定时任务类型。
* 在 agent.js 中继承 agent.ScheduleStrategy,然后通过 agent.schedule.use() 注册即可:
*/
// class ClusterStrategy extends agent.ScheduleStrategy {
// start() {
// // 订阅其他的分布式调度服务发送的消息,收到消息后让一个进程执行定时任务
// // 用户在定时任务的 schedule 配置中来配置分布式调度的场景(scene)
// agent.mq.subscribe(this.schedule.scene, () => this.sendOne());
// }
// }
// agent.schedule.use('cluster', ClusterStrategy);


};
51 changes: 51 additions & 0 deletions api/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
class AppBootHook {
constructor(app) {
this.app = app;
}

configWillLoad() {
// // 此时 config 文件已经被读取并合并,但是还并未生效
// // 这是应用层修改配置的最后时机
// // 注意:此函数只支持同步调用
// // 例如:参数中的密码是加密的,在此处进行解密
// this.app.config.mysql.password = decrypt(this.app.config.mysql.password);
// // 例如:插入一个中间件到框架的 coreMiddleware 之间
// const statusIdx = this.app.config.coreMiddleware.indexOf('status');
// this.app.config.coreMiddleware.splice(statusIdx + 1, 0, 'limit');
}

async didLoad() {
// // 所有的配置已经加载完毕
// // 可以用来加载应用自定义的文件,启动自定义的服务
// // 例如:创建自定义应用的示例
// this.app.queue = new Queue(this.app.config.queue);
// await this.app.queue.init();
// // 例如:加载自定义的目录
// this.app.loader.loadToContext(path.join(__dirname, 'app/tasks'), 'tasks', {
// fieldClass: 'tasksClasses',
// });
}

async willReady() {
// // 所有的插件都已启动完毕,但是应用整体还未 ready
// // 可以做一些数据初始化等操作,这些操作成功才会启动应用
// // 例如:从数据库加载数据到内存缓存
// this.app.cacheData = await this.app.model.query(QUERY_CACHE_SQL);
const ctx = this.app.createAnonymousContext();
ctx.service.template.init();
}

async didReady() {
// 应用已经启动完毕
}

async serverDidReady() {
// http / https server 已启动,开始接受外部请求
// 此时可以从 app.server 拿到 server 的实例
// this.app.server.on('timeout', (socket) => {
// // handle socket timeout
// });
}
}

module.exports = AppBootHook;
2 changes: 1 addition & 1 deletion api/app/controller/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class AccountController extends Controller {
{
account: { type: 'string', required: true },
nick_name: { type: 'string', required: true },
pwd: { type: 'password', required: true },
pwd: { type: 'password', required: true ,message:"密码格式不正确" },
},
input
);
Expand Down
137 changes: 133 additions & 4 deletions api/app/controller/doppelganger.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,142 @@
* 数字分身相关
*/

'use strict';
"use strict";

const { Controller } = require('egg');
const { Controller } = require("egg");

class DoppelgangerController extends Controller {
async create(){}
async getList(){}
async create() {
const { ctx, app } = this;
const input = ctx.request.body;
// 验证参数合法性
const errors = app.validator.validate(
{
// name: { type: 'string', max: 50},
type: { type: "enum", values: [1, 2] },
train_imgs: {
type: "array",
required: true,
itemType: "string",
rule: { type: "string", allowEmpty: false },
},
},
input
);
if (errors && errors.length > 0)
throw ctx.ltool.err(`"${errors[0].field}"${errors[0].message}`, 40011);

// 压缩图片(这里比较耗时,后期可以考虑放到队列中)
let train_imgs = [];
for (let index = 0; index < input.train_imgs.length; index++) {
let origin_img = input.train_imgs[index];
let compress_img_path = `/r/doppelganger/${ctx.user.id}/compress/`;
let format_img_path = `/r/doppelganger/${ctx.user.id}/format/`;
let compress_img = await ctx.service.doppelganger.compressUploadImg(
origin_img,
compress_img_path,
350,
0,
85
);
let format_img = await ctx.service.doppelganger.compressUploadImg(
origin_img,
format_img_path,
768,
768,
85
);
train_imgs.push({
compress_img: compress_img,
origin_img: origin_img,
format_img: format_img,
});
}

let doppelganger = await ctx.service.doppelganger.create(
ctx.user.id,
input.name,
input.type,
train_imgs
);
if (!doppelganger) throw ctx.ltool.err("创建数字分身失败", 40012);
ctx.body = doppelganger._id;
}

async getList() {
const { ctx, app } = this;
const input = ctx.request.query;
// 验证参数合法性
const errors = app.validator.validate(
{
last_id: { type: "string", required: false, max: 24, min: 24 },
},
input
);
if (errors && errors.length > 0)
throw ctx.ltool.err(`"${errors[0].field}"${errors[0].message}`, 40011);
let list = await ctx.service.doppelganger.getList(
ctx.user.id,
input.last_id
);
ctx.body = list;
}
/**
* 删除某个数字分身
*/
async del() {
const { ctx, app } = this;
const input = ctx.request.body;
// 验证参数合法性
const errors = app.validator.validate(
{
id: { type: "string", required: false, max: 24, min: 24 },
},
input
);
if (errors && errors.length > 0)
throw ctx.ltool.err(`"${errors[0].field}"${errors[0].message}`, 40011);
let doppelganger = await ctx.service.doppelganger.getInfo(input.id);
if (!doppelganger || doppelganger.user_id != ctx.user.id)
throw ctx.ltool.err("删除数字分身失败", 40012);

await ctx.service.doppelganger.del(input.id);

ctx.body = "success";
}

/**
* 获得用户可用分身的数量
*/
async getUserCount(){
const { ctx, app } = this;
let count = await ctx.service.doppelganger.getUserCount(ctx.user.id);
ctx.body = count;
}

async getCosToken() {
const { ctx, app } = this;
const input = ctx.request.query;
// 验证参数合法性
const errors = app.validator.validate(
{
ext: {
type: "enum",
values: ["jpg", "jpeg", "png", "gif", "bmp"],
required: true,
},
},
input
);
if (errors && errors.length > 0)
throw ctx.ltool.err(`"${errors[0].field}"${errors[0].message}`, 40011);

let res = await ctx.service.tencentCos.getUniDoppelgangerUploadInfo(
ctx.user.id,
input.ext
);
ctx.body = res;
}
}

module.exports = DoppelgangerController;
Loading

0 comments on commit f115013

Please sign in to comment.