第 5 课 照片变漫画:本地照片转 Base64 地址,交给豆包图生图 | 大模型多模态专题

返回专题

做完你会得到什么

一个「照片变漫画」App:拍一张或选一张照片,点「日系动漫」「水彩插画」「像素游戏」「3D 黏土」任意一个,十几秒后得到同一个画面、不同画风的新图。

照片变漫画界面预览

图为执行最终 .aia 里的真实积木生成的布局预览,不是真机截图;AI 的回答与照片、生成的图片都是示意。

难度 用时 前置 用到的模型
★★★ 30 分钟 第 4 课 AI 画图 豆包 Seedream 生图模型

下载本课 .aia(PhotoComic.aia)

关键问题:本地照片没有网址

第 4 课的图生图,参考图是上一版的网址。可手机里拍的照片只是一个本地文件,GenerateImageFromUrl 收不了文件路径。

办法是把图片本身写进地址里:图片的字节转成 Base64 文本,前面加上 data:image/jpeg;base64,,就成了一个「自带内容」的图片地址,生图接口能直接认。

data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgG……(很长)

图片处理组件正好有现成的方法:

方法 作用
压缩(图片, 最长边, 质量) 缩小尺寸、存成 JPEG,返回新文件路径
转Base64(图片) 把文件内容转成 Base64 文本(不带 data: 前缀)

一定要先压缩再转。Base64 比原文件还大三分之一,一张 4MB 的原图转出来 5MB 多的文本,上传又慢,还可能超过接口的大小限制。压到 1024 再转,只有一两百 KB。

界面组件

组件 命名 关键属性 / 作用
按钮 / 图像选择器 按钮_拍照、图像选择器_相册  
图像 图像_原图、图像_成品 原图高 200,成品高 300
按钮 按钮_日漫、按钮_水彩、按钮_像素、按钮_黏土 两行两列
Doubao(拓展) Doubao1 默认属性即可
照相机、图片处理 照相机1、图片处理1  

API Key 卡片与第 4 课相同(标签名 doubaoKey,两课共用一个火山方舟 Key)。

分步搭

第 1 步:照片 → 压缩 → Base64 地址

global 原图地址 = ""
global 正在画 = false
global 当前风格 = ""

when 按钮_拍照.Click() {
  照相机1.TakePicture()
}

when 照相机1.AfterPicture(image) {
  准备原图(image)
}

when 图像选择器_相册.AfterPicking() {
  准备原图(图像选择器_相册.Selection)
}

procedure 准备原图(图片) {
  if 正在画 {
    底部提示条1.Show("还在画,稍等一下再换照片")
  } else {
    // 先压缩再转 Base64:原图几 MB,转出来的文本更大,会超过接口上限
    var 小图 = 图片处理1.Compress(图片, 1024, 85)
    var 编码 = choose(小图 == "", "", 图片处理1.ToBase64(小图))
    if 编码 == "" {
      底部提示条1.Show("这张图片处理不了,换一张试试")
    } else {
      原图地址 = join("data:image/jpeg;base64,", 编码)
      图像_原图.Picture = 小图
      图像_成品.Picture = ""
      标签_状态.Text = "照片选好了,点上面任意一种风格。"
    }
  }
}

压缩总是存成 JPEG,所以前缀固定写 image/jpeg。choose(小图 == "", "", …) 的意思是:压缩失败就不再转 Base64,直接当失败处理。

中途验证:选一张照片,原图位置显示压缩后的照片,状态行提示选风格。

第 2 步:四种风格,只换一个词

四个按钮调用同一个过程,传不同的风格名。提示词的其余部分完全一样,强调要保留什么:人物、姿势、构图、背景布局、五官特征。不说的话,模型常常只借用一下颜色,画出一张完全不同的图。

when 按钮_日漫.Click() {
  变身("日系动漫")
}

when 按钮_水彩.Click() {
  变身("水彩插画")
}

when 按钮_像素.Click() {
  变身("像素游戏")
}

when 按钮_黏土.Click() {
  变身("3D 黏土动画")
}

procedure 变身(风格) {
  if 正在画 {
    底部提示条1.Show("还在画上一张,稍等一下")
  } else if Doubao1.APIKey == "" {
    底部提示条1.Show("先在上面填好 API Key 并点保存")
  } else if 原图地址 == "" {
    底部提示条1.Show("先拍一张或选一张照片")
  } else {
    当前风格 = 风格
    正在画 = true
    设置按钮(false)
    标签_状态.Text = join("正在画成", 风格, "风格……")
    Doubao1.GenerateImageFromUrl(join("把这张照片转换成", 风格, "风格。保留原图的人物、姿势、构图和背景布局,人物五官特征保持可辨认,画面干净,不要添加任何文字。"), 原图地址)
  }
}

procedure 设置按钮(可用) {
  按钮_日漫.Enabled = 可用
  按钮_水彩.Enabled = 可用
  按钮_像素.Enabled = 可用
  按钮_黏土.Enabled = 可用
  按钮_拍照.Enabled = 可用
  图像选择器_相册.Enabled = 可用
}

想加更多风格,复制一个按钮、改一个词:「宫崎骏动画」「赛博朋克」「中国水墨」「乐高积木」「美式漫画」……

第 3 步:显示结果

when Doubao1.GotImage(imageUrl) {
  正在画 = false
  设置按钮(true)
  图像_成品.Picture = imageUrl
  标签_状态.Text = join("变好了:", 当前风格, "风格。换一种风格再点一次。图片链接有时效,想留着可以照第 4 课的办法下载保存。")
}

when Screen1.ErrorOccurred(component, functionName, errorNumber, message) {
  正在画 = false
  设置按钮(true)
  标签_状态.Text = 错误说明(message)
}

function 错误说明(message) {
  return choose(containstext(message, "401"), "API Key 不对:检查是否抄全,或到火山方舟重新创建。", choose(containstext(message, "ModelNotOpen"), "生图模型还没开通:到火山方舟「开通管理」里开通 Seedream 后再试。", choose(containstext(message, "Sensitive"), "照片或描述被判定为不适合生成的内容,换一张试试。", choose(containstext(message, "429"), "请求太频繁了,等几秒再试。", join("出错了:", message)))))
}

中途验证:点「日系动漫」,所有按钮变灰;十几秒后成品出现,按钮恢复;再点「像素游戏」,同一张原图换了画风。

这一课的要点

  • 「本地图片 → 地址」万能公式:join("data:image/jpeg;base64,", 图片处理.转Base64(图片处理.压缩(图片, 1024, 85)))。凡是只收图片网址的接口都能这样喂本地照片——包括识图的 ChatWithImageUrl。
  • 先压缩再编码,Base64 只会让体积变大。
  • 图生图的提示词要说「保留什么」,不只是「变成什么」。
  • 和第 4 课比,这里每次都以原图为参考(不是上一张结果),所以换风格不会越变越走样。

常见问题

现象 原因与处理
提示「这张图片处理不了」 选到的不是图片,或文件已被删除。换一张
报错大意是「图片太大」「请求体过大」 没压缩就转了 Base64。照第 1 步先压缩到 1024
结果和原图毫无关系 提示词里没写「保留原图的人物、姿势、构图」;或者照片主体太小,换一张主体清楚的
人脸不像本人了 风格化必然会改变五官。「五官特征保持可辨认」能改善,但做不到照片级还原
状态行「被判定为不适合生成的内容」 照片里有审核敏感的内容(比如某些公众人物、证件),换一张
手机里看原图是横的,传过去变成竖的 不会:压缩会按照片的拍摄方向自动转正

平台说明

  • 安卓、鸿蒙都能运行,鸿蒙的图片处理组件同样提供压缩和转 Base64。
  • 业务检查见验证记录:Base64 地址解出来就是压缩后的文件、编码后体积、四种风格提示词只差风格名、生成期间不能换照片等都逐条执行过。

下一课

第 6 课 视频摘要:从图片升级到视频——给通义千问一个视频链接,让它写摘要、列时间线、出理解题。

文档反馈