用Nano Banana生成透明背景图像
最近我一直在尝试使用新的 Nano Banana Pro 2。
多么棒的模型!图像质量令人难以置信,但像大多数图像模型一样(除了 gpt-image),它有一个对开发者来说很大的问题:它不支持透明度。
如果你要求透明背景,你会得到一个纯白色、黑色或棋盘格的背景。它输出的是没有 Alpha 通道的平面 JPEG/PNG。
以下是我如何从 Nano Banana Pro 2 中获取透明素材的方法。
1、尝试 "绿幕"方法
我的第一个直觉是经典的 VFX 方法:
生成一个未来主义头盔,上面有大写文字"HERE IS A HELMET",下方有阴影,背景是纯实的 #00FF00 绿色
我假设我可以像天气预报员一样把绿色抠掉。
但这种技术有它的缺点:
- 绿色泄漏: AI 模型倾向于在生成的图像中加入绿色,因为绿色提示会泄漏到生成过程中。
- 伪影: AI 模型难以处理大面积的数学上完全平坦的颜色区域。"纯实绿色"实际上是由略微不同的绿色组成的噪点纹理,使得魔棒工具毫无用处。
- 光晕: 在某些情况下,如文字或 UI 元素,我会发现所有东西周围都有一个锯齿状的 1px 绿色光晕。以一种可靠的系统性方式消除这一点很困难。
我意识到色度抠图并不能为透明度提供可靠的、干净的方法。
2、解决方案:差异遮罩
理论很简单:如果你有一个完全相同的像素显示在纯黑背景和纯白背景上,你可以使用数学来计算那个像素有多透明。
Nano Banana Pro 2 在图像编辑方面表现出色,所以我想到"嘿,这可能是可行的!"
3、工作流程
技巧在于模型的图像编辑能力。
步骤 1:在白色背景上生成
首先,我用标准提示生成素材。
一个复杂、详细的未来主义 UI,类似 HUD,有不同的组件,透明效果,背景是纯实的白色 #FFFFFF
步骤 2:编辑为黑色背景
我将输出结果反馈回模型,并附上编辑请求。
将白色背景改为纯实的黑色 #000000。保持其他所有内容完全不变
因为模型在编辑过程中会尝试保留主题,所以我最终得到两个完美对齐的图像:一个在白色背景上,一个在黑色背景上。
步骤 3:应用差异遮罩
现在让我们使用这两张图像来创建最终的透明图像:
npx tsx extract_alpha.ts "input-white.jpeg" "input-black.jpeg" "output.png"
结果绝对完美!
4、代码
我写了一个使用 sharp 库处理这两张图像的小型 TypeScript 工具。它逐像素比较它们以恢复 Alpha 通道。
以下是我使用的函数:
import sharp from 'sharp';
import fs from 'fs/promises';
export interface RGB { r: number; g: number; b: number; }
const BLACK: RGB = { r: 0, g: 0, b: 0 };
const WHITE: RGB = { r: 255, g: 255, b: 255 };
export async function extractAlphaTwoPass(
imgOnWhitePath: string,
imgOnBlackPath: string,
outputPath: string
): Promise<void> {
const img1 = sharp(imgOnWhitePath);
const img2 = sharp(imgOnBlackPath);
// 确保我们处理的是原始像素数据
const { data: dataWhite, info: meta } = await img1
.ensureAlpha()
.raw()
.toBuffer({ resolveWithObject: true });
const { data: dataBlack } = await img2
.ensureAlpha()
.raw()
.toBuffer({ resolveWithObject: true });
if (dataWhite.length !== dataBlack.length) {
throw new Error("尺寸不匹配:图像必须大小相同");
}
const outputBuffer = Buffer.alloc(dataWhite.length);
// 白色 (255,255,255) 和黑色 (0,0,0) 之间的距离
// sqrt(255^2 + 255^2 + 255^2) ≈ 441.67
const bgDist = Math.sqrt(3 * 255 * 255);
for (let i = 0; i < meta.width * meta.height; i++) {
const offset = i * 4;
// 获取两个图像中相同像素的 RGB 值
const rW = dataWhite[offset];
const gW = dataWhite[offset + 1];
const bW = dataWhite[offset + 2];
const rB = dataBlack[offset];
const gB = dataBlack[offset + 1];
const bB = dataBlack[offset + 2];
// 计算两个观察到的像素之间的距离
const pixelDist = Math.sqrt(
Math.pow(rW - rB, 2) +
Math.pow(gW - gB, 2) +
Math.pow(bW - bB, 2)
);
// 公式:
// 如果像素 100% 不透明,它在黑色和白色上看起来一样 (pixelDist = 0)。
// 如果像素 100% 透明,它看起来完全像背景 (pixelDist = bgDist)。
// 因此:
let alpha = 1 - (pixelDist / bgDist);
// 将结果限制在 0-1 范围内
alpha = Math.max(0, Math.min(1, alpha));
// 颜色恢复:
// 我们使用黑色背景上的图像来恢复颜色,除以 alpha
// 来取消预乘(提亮半透明像素)
let rOut = 0, gOut = 0, bOut = 0;
if (alpha > 0.01) {
// 从黑色版本恢复前景颜色
// (C - (1-alpha) * BG) / alpha
// 由于 BG 是黑色 (0,0,0),这简化为 C / alpha
rOut = rB / alpha;
gOut = gB / alpha;
bOut = bB / alpha;
}
outputBuffer[offset] = Math.round(Math.min(255, rOut));
outputBuffer[offset + 1] = Math.round(Math.min(255, gOut));
outputBuffer[offset + 2] = Math.round(Math.min(255, bOut));
outputBuffer[offset + 3] = Math.round(alpha * 255);
}
await sharp(outputBuffer, {
raw: { width: meta.width, height: meta.height, channels: 4 }
})
.png()
.toFile(outputPath);
}
5、为什么这很棒
结果让我震惊。它是一个完美的透明图像。
- 玻璃有效: 我生成的房屋中半透明窗户实际上变得透明了。
- 阴影有效: 物体投射的微弱阴影被保留为带有低 Alpha 值的黑色像素,这意味着我可以将这些素材放在任何背景颜色上,阴影都能正确混合。
- 没有绿色光晕: 由于我们不是抠出颜色,所以没有锯齿状的彩色边缘。
- 更少伪影风险: 黑色和白色背景在大面积纯色区域上不易产生伪影。
原文链接: Generating transparent background images with Nano Banana Pro 2
汇智网翻译整理,转载请标明出处