和妹子逛完街,写了个 AI 智能穿搭系统

想直接看成品演示的可以直接划到文章底部

背景

故事起源在和一个妹子去逛衣服店的时候,试来试去的难以取舍,最终消耗了我一个小时。虽然这个时间不多,
但这个时间黑神话悟空足矣让我打完虎先锋

回家我就灵光一闪,是不是可以搞一个AI智能穿搭,只需要上传自己的照片和对应的衣服图片就能实现在线试衣服呢?

说干就干,我就开始构思方案,画原型。
俗话说万事开头难,事实上这个构思到动工就耗费了我一个礼拜,因为一直在构思怎么样的交互场景会让用户使用起来比较丝滑,并且容易上手。

目前实现的功能有:

  • ✅ 用户信息展示
  • ✅ AI 生成穿搭
  • ✅ 风格大厅

待完成:

  • 私人衣柜
  • AI 换鞋

经过

1. 画产品原型

起初第一个版本的产品原型由于是自己构思没有任何参考,直接上手撸代码的,想到啥就画啥,所以布局非常传统,配色也非常普通(蚂蚁蓝),所以感觉没有太多的时尚气息(个人觉得丑的一逼,不像是互联网的产物)。因为重构掉了,老的现在没有了,我懒就不重新找回来截图了,直接画个当时的样子,大概长成下面这样:

image-20240829104107115

丑的我忍不了,我就去设计师专门用的网站参(chao)考(xi)了一下,找来找去,终于有了下面的最终版原型图

image-20240829104148447

2. 配色选择

大家知道,所有的UI设计,都离不开主题色的选择,比如:淘宝橙、飞猪橙、果粒橙…,目的一方面是为了打造品牌形象,另一方面也是为了提升品牌辨识度,让你看到这个颜色就会想起它

那我必须也得跟上时代的潮流,选了 $#c1a57b$ 这款低调而又不失奢华的色值作为主题色,英雄不问出处,问就是借鉴。

3. 技术选型

我对技术的定义是:技术永远服务于产品,能高效全面帮助我开发出一款应用,并且能保证后续的稳定性和可维护性,啥技术我都行。当然如果这门技术我优先会从我属性的板块去找。

经过各种权衡和比较,最后敲定下来了技术选型方案:

  • 前端:taro (为了后续可能会有小程序端做准备)
  • 后端:koajs (实际使用的是midway,基于koajs,主要是比较喜欢koa的轻量化架构)
  • 数据库:mongodb (别问,问就是简单易上手)
  • 代码仓库:gitea
  • CI:gitea-runner
  • 部署工具:pm2
  • 静态文件托管:阿里云OSS

4. 撸代码

这里我只挑一些个人感觉相对需要注意的地方展开讲讲

4.1 图片转存

由于我生成图片的API图片链接会在一天之后失效,所以我需要在调用任务详情的时候,把这个文件转存到我自己的oss服务器,这里我总结出来的思路是:【1. 保存在本地暂存文件夹】-【2. 调用node流式读取接口】-【3. 保存到oss】-【4. 返回替换原来的链接】

具体代码参考如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const tempDir = path.join(tmpdir(), 'temp-upload-files')
const link = url.parse(src);
const fileName = path.basename(link.pathname)
const localPath = path.join(tempDir, `/${fileName}`); // 生成保存路径
let request
if (link.protocol === 'https:') {
request = https
} else {
request = http
}
request.get(src, async (response) => {
const fileStream = await fs.createWriteStream(localPath); // 保存到本地暂存路径

await response.pipe(fileStream);
fileStream.on("error", (error) => {
console.error("保存图片出错:", error);
reject(error)
});
fileStream.on('finish', async res => {
console.log('暂存完成,开始上传:', res)
let result = await this.ossService.put(`/${params.saveDir || 'tmp'}/${fileName}`, localPath);
if (!result) return
resolve(result)
});
});

这里的request因为我不想引入其它的库所以这样写,如果有更好的方案,可以在评论区告知一下。

这里需要注意的一个地方是,上传的这个 localPath 最好是自己做一下处理,我这边没有处理,因为可能两个用户同时上传,他们的文件名称相同的时候,可能会出现覆盖的情况,包括后面的oss保存也是。

4.2 文件流式上传中间件

因为默认的接口处理是不处理流式调用的,所以需要自己创建一个中间件来拦截处理一下,下面给出我的参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
class SSE {
ctx: Context
constructor(ctx: Context) {
ctx.status = 200;
ctx.set('Content-Type', 'text/event-stream');
ctx.set('Cache-Control', 'no-cache');
ctx.set('Connection', 'keep-alive');
ctx.res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Transfer-Encoding': 'chunked'
});
ctx.res.flushHeaders();
this.ctx = ctx;
}
send(data: any) {
// string
if (typeof data === "string") {
this.push(data);
} else if (data.id) {
this.push(`id: ${data.id}\n`);
} else if (data.event) {
this.push(`event: ${data.event}\n`);
} else {
const text = JSON.stringify(data)
this.push(`data: ${text}\n\n`);
}
}
push(data: any) {
this.ctx.res.write(data);
this.ctx.res.flushHeaders();
}
close() {
this.ctx.res.end();
}
}

@Middleware()
export class StreamMiddleware implements IMiddleware<Context, NextFunction> {
// ?------------ 中间件处理逻辑 -----------------
resolve() {
return async (ctx: Context, next: NextFunction) => {

if (ctx.res.headersSent) {
if (!ctx.sse) {
console.error('[sse]: response headers already sent, unable to create sse stream');
}
return await next();
}

const sse = new SSE(ctx);
ctx.sse = sse;
await next();

if (!ctx.body) {
ctx.body = ctx.sse;
} else {
ctx.sse.send(ctx.body);
ctx.body = sse;
}
};
}

public match(ctx: Context): boolean {
// ?------------ 不带 stream 前缀默认都不是流式接口 -----------------
if (ctx.path.indexOf('stream') < 0) return false
}

static getName(): string {
return 'stream';
}
}

4.3 mongodb 数据库的权限

这里尽量不要使用root权限的数据库角色,可以创建一个只有当前数据库权限的角色,具体可以网上找相关文档,怎么为某个collection创建账户。

实机演示

1. 提交素材,创建任务

2. 获取生成图片

3. 展示大厅(待完善)

image-20240829104459820

结语

当然现在目前这个还是内测版本,功能还不够健全,还有很多地方需要打磨,包括用户信息页面的展示是否合理,UI的排版,数据库表的设计等等

通过观察生活用现有的技术创造一些价值,对我来说就是一种幸福且有意义的事儿。

如果想要体验的可以后台私信我。如果你也有很棒的想法想交流一下,也可以私我。

我是dev,下期见(太懒了我,更新频率太低)

个人博客

灵感中心

和妹子逛完街,写了个 AI 智能穿搭系统

http://blog.jzxer.cn/20240827/20240827-Ai_jiyi_start/

作者

dev

发布于

2024-08-27

更新于

2024-11-29

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×