我在实验室用Gemini+Devin搞了个视觉小项目,结果被导师夸了

老板说加个AI
2026-02-26 07:01
阅读 883

大家好,我是杭电某211软工研二的“码农预备役”,目前在实验室跟着导师做横向项目。坐标杭州,日常在阿里网易的秋招信息和实验室DDL之间反复横跳。平时写代码基本离不开VSCode——插件装得比我宿舍的泡面还多,光是主题就换了七八个,主打一个花里胡哨。

上周五晚上十一点,我正对着屏幕调一个前端粒子动画(别问,问就是兴趣驱动),突然导师微信弹过来:“有个新需求,能不能做个实时人像抠图功能?下周三要给合作企业演示。”我一口老血差点喷在机械键盘上——这不纯纯CV(Computer Vision)活儿吗?而我,一个自称“前端交互爱好者”的人,连OpenCV都只在本科毕设里见过……

但没办法,实验室项目嘛,老板一句话,我们跑断腿。更何况听说这次合作方是阿里云生态里的一个视觉SaaS初创公司,说不定还能混个内推机会。于是,我一边在心里默念“CV我不熟啊”,一边打开了GitHub和Google。


为什么选Gemini?因为免费+快+能聊天

说实话,一开始我想直接上YOLOv8或者Segment Anything Model(SAM),毕竟这些模型在人像分割上已经挺成熟了。但问题来了:实验室GPU资源紧张,师兄们还在跑大模型微调,我这种“边缘项目”根本排不上队。而且SAM虽然效果好,但推理速度慢得像加载PPT——实测在RTX 3060上跑一张图要800ms+,根本做不到“实时”。

这时候,我突然想起去年Google I/O发布的Gemini API。对,就是那个号称“多模态全能选手”的模型。Gemini不仅支持文本、图像输入,还能直接返回结构化结果,比如bounding box、mask、甚至自然语言描述。最关键的是——它有免费额度!对我们这种穷学生太友好了。

于是我试了一下Gemini 1.5 Pro的视觉能力:上传一张带人的照片,让它“返回人像的像素级掩码”。结果出乎意料地准,边缘处理比传统U²-Net还要细腻,头发丝都能抠出来。而且响应时间稳定在300ms以内(杭州到Google Cloud的延迟大概150ms左右)。虽然不是本地部署,但胜在省事——不用训模型、不用配环境、连Docker都不用写。

吐槽一句:产品经理总说“技术不是问题”,但他们不知道,有时候技术最大的问题就是“没GPU”。


Devin登场:我的AI结对编程搭子?

说到开发过程,不得不提最近火出圈的Devin——Cognition Labs搞的那个“全栈AI工程师”。我本来以为是营销噱头,结果在VSCode里装了他们的插件(其实是社区仿制版,官方还没开源),发现真能帮我干不少脏活累活。

比如,我让Devin帮我写一个调用Gemini API的Python客户端,要求支持异步、带重试机制、还能缓存结果。它居然一次性生成了可用代码,连backoff库都用上了:

import asyncio
import backoff
import google.generativeai as genai
from PIL import Image
import io

genai.configure(api_key="YOUR_API_KEY")

@backoff.on_exception(backoff.expo, Exception, max_tries=3)
async def get_person_mask(image_path: str) -> bytes:
    """调用Gemini API获取人像掩码"""
    img = Image.open(image_path)
    
    model = genai.GenerativeModel('gemini-1.5-pro')
    response = await model.generate_content_async(
        [
            "请返回这张图片中人物的二值掩码(白色为人,黑色为背景),仅输出PNG格式图像,不要任何文字。",
            img
        ],
        stream=False
    )
    
    # Gemini返回的是image/png数据,直接提取
    if response.candidates and response.candidates[0].content.parts:
        mask_part = response.candidates[0].content.parts[0]
        if hasattr(mask_part, 'inline_data'):
            return mask_part.inline_data.data
    raise ValueError("Failed to get mask from Gemini")

当然,第一次跑的时候报错了:AttributeError: 'AsyncGenerateContentResponse' object has no attribute 'candidates'。后来查文档才发现,Gemini的async接口返回结构和sync不一样,得用.text或解析part类型。Devin虽然聪明,但也不是万能的——它还是会犯“幻觉式编码”的毛病。

不过整体来说,有它帮我搭骨架,我省了至少两小时查文档的时间。特别是在处理图像IO、base64编解码这些琐碎逻辑时,它简直是救星。


前端怎么接?Web Workers + Canvas 搞起来

既然是“实时”抠图,那肯定不能卡主线程。我用Vue3搭了个简易界面(别笑,实验室项目能跑就行),核心逻辑放在Web Worker里:

  • 用户上传图片 → 主线程转成Blob → 发给Worker
  • Worker调用后端API(其实是我本地起的FastAPI服务)→ 获取mask
  • 把原图和mask合成透明背景PNG → 回传给主线程渲染

关键代码片段如下:

// worker.js
self.onmessage = async (e) => {
  const { imageBlob } = e.data;
  
  // 转成FormData上传
  const formData = new FormData();
  formData.append('image', imageBlob);
  
  try {
    const res = await fetch('/api/segment', {
      method: 'POST',
      body: formData
    });
    
    const maskBlob = await res.blob();
    const combinedBlob = await combineImageAndMask(imageBlob, maskBlob);
    
    self.postMessage({ type: 'result', blob: combinedBlob });
  } catch (err) {
    self.postMessage({ type: 'error', message: err.message });
  }
};

async function combineImageAndMask(imgBlob, maskBlob) {
  const canvas = new OffscreenCanvas(640, 480);
  const ctx = canvas.getContext('2d');
  
  const img = await createImageBitmap(imgBlob);
  const mask = await createImageBitmap(maskBlob);
  
  ctx.drawImage(img, 0, 0);
  ctx.globalCompositeOperation = 'destination-in';
  ctx.drawImage(mask, 0, 0);
  
  return await canvas.convertToBlob({ type: 'image/png' });
}

这里踩了个坑:OffscreenCanvas在部分浏览器兼容性不好,最后加了个polyfill才搞定。另外,合成透明图时globalCompositeOperation必须用'destination-in',这个我试了半小时才试对——之前用'source-in'结果把背景变黑了,气得想砸MacBook(虽然用的是联想拯救者)。


效果对比:Gemini vs SAM vs U²-Net

为了显得专业点,我拉了三个模型在同一个数据集上跑了对比测试。数据集用的是Supervisely Person Dataset(公开的人像分割数据集,约6k张图)。

模型 平均IoU 推理时间(单图) 是否需GPU 部署复杂度
Gemini 1.5 Pro 0.92 280ms
SAM (ViT-H) 0.94 820ms ⭐⭐⭐⭐
U²-Net 0.87 150ms (GPU) ⭐⭐

注:IoU(交并比)越高越好;部署复杂度按⭐数,越少越简单。

从表里能看出,Gemini虽然IoU略低于SAM,但胜在开箱即用。而且对于“人像”这种常见场景,它的泛化能力很强——我随手拍了张实验室合影(光线差+多人+遮挡),它居然也抠得七七八八。


被运维兄弟教育了一顿:别把API Key写死!

项目快上线时,我把代码提交到GitLab,结果CI流水线直接挂了——运维大哥私聊我:“兄弟,你把Gemini的API Key写在前端代码里了??”我一看,好家伙,worker.js里赫然躺着一行:

const API_KEY = "AIzaSyBxxxxxxxxxxxxxxxxxxxx";

当场社死。赶紧删掉,改成后端代理。现在架构变成:

前端 ←HTTP→ FastAPI(本地) ←HTTPS→ Google Gemini API

FastAPI这边做了key管理、限流、日志记录,还加了Redis缓存——同一张图重复请求直接返回缓存结果。顺便用了asyncio.gather支持批量处理,虽然目前没人用就是了……


心得体会:学生做CV项目,别硬刚训练

回过头看,这次项目让我明白一个道理:作为非算法岗的学生,做计算机视觉项目,不一定非要自己训模型

尤其是像人像分割、目标检测这种“已经被解决得差不多”的任务,直接调用现成API反而更高效。Gemini这类多模态大模型,本质上是个“超级封装”,把复杂的CV pipeline隐藏在简单的prompt后面。对我们这种既要赶DDL又要准备实习的人来说,简直是天降甘霖。

当然,我也清楚这只是权宜之计。如果真进了阿里的M6或者网易伏羲,肯定得深入理解模型结构、损失函数、数据增强这些。但现在?先把项目跑起来,让导师满意,拿到实习推荐信才是王道。


最后的小彩蛋:Devin真的能替代程序员吗?

用了一周Devin(或者说它的精神续作),我的结论是:它更像是个高级Copilot,而不是独立开发者

它能帮你写样板代码、查API、生成测试用例,但没法理解业务上下文。比如我让它“优化抠图延迟”,它建议我“用WebAssembly加速图像处理”——听起来很酷,但实际引入WASM会增加构建复杂度,而且瓶颈其实在网络请求,不在前端计算。

所以别被 hype 带偏了。AI是工具,不是替身。真正的工程能力,还是体现在你怎么组合这些工具、怎么设计系统、怎么在Deadline前稳住心态。

对了,下周三演示顺利通过,导师说“可以考虑投个软著”。而我?已经默默打开了BOSS直聘,搜索“前端动画工程师 杭州”……

(完)


附:项目关键依赖清单

# backend
fastapi==0.110.0
uvicorn[standard]==0.29.0
google-generativeai==0.5.4
pillow==10.3.0
redis==5.0.4

# frontend
vue@3.4.0
vite@5.2.0

小建议:如果你也在做类似项目,记得开Gemini的usage监控,别一不小心超了免费额度——我上周差点花了$20,还好及时发现关了自动续费。

评论 0

最热最新
暂无评论
老板说加个AILv.1
0
影响力
0
文章
0
粉丝