You need to enable JavaScript to run this app.
文档中心
图像生成大模型

图像生成大模型

复制全文
下载 pdf
最佳实践
Inpainting&OutPainting最佳实践
复制全文
下载 pdf
Inpainting&OutPainting最佳实践
效果图​
​
​
原图​
涂抹​
获取mask​
涂抹消除​
涂抹编辑​
智能扩图(比例)​
智能扩图(画布)​
​图片​​
​图片​​
​图片​​
​图片​​
​图片​​
​图片​​
​图片​​
​
Inpainting​
涂抹/识别需要操作的对象​
前端涂抹Demo​
  • 需要修改image.src = 'origin_painting.png';​
  • 需要设置canvas的尺寸与图片的尺寸一致;// 设置canvas的尺寸​
  • 涂抹完成后,右键保存到本地或上传到云端生成http链接​
​
<!DOCTYPE html>​
<html>​
​
<head>​
<title>Canvas Smear Mask Example</title>​
<style>​
body {​
position: relative;​
margin: 0;​
padding: 0;​
}​
#controls {​
position: absolute;​
left: 10px;​
top: 10px;​
z-index: 10;​
}​
canvas {​
border: 1px solid black;​
cursor: crosshair;​
display: block; /* 避免出现滚动条 */​
margin: 0 auto; /* 水平居中画布 */​
position: relative; /* 相对定位使其能够正确计算offsetLeft和offsetTop值 */​
top: 20px; /* 按钮高度+间距 */​
}​
</style>​
<script>​
window.onload = function () {​
var canvas = document.getElementById('smear-canvas');​
var ctx = canvas.getContext('2d');​
var mouseDown = false;​
​
// 设置canvas的尺寸,与待涂抹的原图尺寸一致​
canvas.width = 1000;​
canvas.height = 1000;​
​
// 加载一个图片作为背景​
var image = new Image();​
image.onload = function () {​
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);​
}​
image.src = '女侧119K.JPG'; // 你的图片路径​
​
// 当鼠标在canvas上按下时开始涂抹​
canvas.addEventListener('mousedown', function (e) {​
mouseDown = true;​
ctx.globalCompositeOperation = 'destination-out'; // 设置涂抹后显示下面的内容​
});​
​
// 当鼠标在canvas上移动时,如果是按下状态,则涂抹​
canvas.addEventListener('mousemove', function (e) {​
if (mouseDown) {​
var x = e.pageX - canvas.offsetLeft;​
var y = e.pageY - canvas.offsetTop;​
ctx.beginPath();​
ctx.arc(x, y, 10, 0, Math.PI * 2, false); // 绘制涂抹的圆形​
ctx.fill();​
}​
});​
​
// 当鼠标释放或离开canvas时,停止涂抹​
canvas.addEventListener('mouseup', function () {​
mouseDown = false;​
});​
canvas.addEventListener('mouseleave', function () {​
mouseDown = false;​
});​
// 添加保存画布内容到本地的功能​
var saveButton = document.getElementById('saveButton');​
saveButton.addEventListener('click', function () {​
var dataURL = canvas.toDataURL('image/png');​
var link = document.createElement('a');​
link.download = '标注结果.png';​
link.href = dataURL;​
link.click();​
});​
};​
</script>​
</head>​
​
<body>​
<div id="controls">​
<button id="saveButton">保存到本地</button>​
</div>​
<canvas id="smear-canvas"></canvas>​
</body>​
​
</html>​
​
抠出黑白mask​
Go 获取mask Demo​
​
package main​
​
import (​
"image"​
"image/color"​
​
// 确保导入了支持的图像格式包​
_ "image/jpeg"​
"image/png"​
"os"​
​
"github.com/cloudwego/hertz/pkg/common/hlog"​
)​
​
// FileUrl2Image 从指定链接获取图片并转成Image​
func FileUrl2Image(fileUrl string) image.Image {​
// 发送get请求,拉取文件​
resp, err := http.Get(fileUrl)​
if err != nil {​
hlog.Errorf("+++++HTTP GET error: %w", err)​
return nil​
}​
defer resp.Body.Close()​
// 检查HTTP响应状态码​
if resp.StatusCode != http.StatusOK {​
hlog.Errorf("+++++HTTP Status not OK: %d", resp.StatusCode)​
return nil​
}​
img, _, err := image.Decode(resp.Body)​
if err != nil {​
hlog.Error("+++++解码图片失败, img:", img)​
hlog.Error("+++++解码图片失败, err:", err)​
}​
return img​
}​
​
​
// CreateMaskBasedOnAlpha 将使用传入图片的alpha通道值来创建一个遮罩​
func createMaskBasedOnAlpha(img image.Image) image.Image {​
// 获取原始图片的边界​
rect := img.Bounds()​
// 创建一个相同大小的mask图像(默认遮罩是全黑的,也就是透明的)​
mask := image.NewAlpha(rect)​
​
// 遍历原始图片的每个像素​
for y := rect.Min.Y; y < rect.Max.Y; y++ {​
for x := rect.Min.X; x < rect.Max.X; x++ {​
// 取出原始图片的像素​
_, _, _, a := img.At(x, y).RGBA()​
// RGBA方法返回的颜色值范围是0-65535​
// 将其转换为0-255范围,以便与通常的alpha值比较。​
alpha := uint8(a >> 8)​
​
// 基于alpha值判断是否应该在遮罩中保留这个像素​
if alpha > 128 { // 假设alpha大于128我们才保留这个像素​
mask.SetAlpha(x, y, color.Alpha{A: 0}) // 不透明​
} else {​
mask.SetAlpha(x, y, color.Alpha{A: 255}) // 完全透明​
}​
}​
}​
return mask​
}​
​
// saveMaskToFile 将遮罩图像保存到文件中​
// mask: 遮罩信息​
// maskSavePath: mask转成图片的路径​
func saveMaskToFile(mask image.Image, maskSavePath string) error {​
// 创建一个文件用于保存遮罩​
ofi, err := os.OpenFile(maskSavePath, os.O_RDWR|os.O_CREATE, os.ModePerm)​
if err != nil {​
return err​
}​
defer ofi.Close()​
​
// 使用png格式将遮罩编码并写入文件​
err = png.Encode(ofi, mask)​
if err != nil {​
return err​
}​
​
return nil​
}​
​
func GenerateMask(img image.Image, maskPath string) {​
// 从文件中解码图片​
err := saveMaskToFile(createMaskBasedOnAlpha(img), maskPath)​
if err != nil {​
hlog.Error("+++++保存mask失败,err:", err)​
}​
}​
​
func main() {​
imgUrl := "https://xxxx" // 更换成第一步涂抹完成的图片链接​
GenerateMask(FileUrl2Image(imgUrl), "test-mask.png") // 此时生成的test-mask.png即为接口调用使用的mask​
}​
​
消除能力​
在完成第1,2步后,开始调用 inpainting涂抹消除接口​
​
// 请求参数Demo​
{​
"binary_data_base64": ["原图", "原图标注后的mask"],​
"req_key": "i2i_inpainting",​
"scale": 7,​
"seed": 0,​
"steps": 30,​
"strength": 0.8​
}​
​
​
// 返回参数Demo​
{​
"code": 10000,​
"data": {​
"algorithm_base_resp": {​
"status_code": 0,​
"status_message": "Success"​
},​
"binary_data_base64": ["消除图base64"],​
"request_id": "740d84695b1a51a5e36f7559a29dbe563f003b99add6c17f419badda749ccd21"​
},​
"message": "Success",​
"request_id": "2024031411153853949433144E29001798",​
"status": 10000,​
"time_elapsed": "3.940519855s"​
}​
​
编辑能力​
在完成第1,2步后,开始调用 inpainting涂抹编辑接口​
​
// 请求参数Demo​
{​
"binary_data_base64": ["原图", "原图标注后的mask"],​
"custom_prompt": "一只小狗", // 写入Prompt,AIGC生成取代的内容​
"req_key": "i2i_inpainting_edit",​
"scale": 5,​
"seed": -1,​
"steps": 25​
}​
​
​
// 返回参数Demo​
{​
"code": 10000,​
"data": {​
"algorithm_base_resp": {​
"status_code": 0,​
"status_message": "Success"​
},​
"binary_data_base64": ["编辑后的图base64"],​
"request_id": "d1741fd2176ae73ea6e17ae911bc81a5d6848449357c7024fe3be30701412726"​
},​
"message": "Success",​
"request_id": "2024031414442379E5503DD25AA800AB0F",​
"status": 10000,​
"time_elapsed": "2.173378731s"​
}​
​
OutPainting​
可选使用方式​
​
类型​
描述​
交互逻辑建议​
top、bottom 、left 、right​
mask​
示例图-原图​
示例图-效果图​
等比扩展​
以图片中心将图片等比扩展,可以设置扩展倍数,考虑效果和性能,目前最大为1​
前端做数个固定比例扩展的按钮如扩展10%、20%,让用户点选扩展比例​
参数同时传相同数值,数值越大延迟越高​
不传​
​图片​​
原图​
​图片​​
扩展1倍​
画幅扩展​
以原图为中心,将图片扩展为特定画幅比例的图片。比如1张1:1的方图,扩展生成16:9的横图。一般默认原图大小不变,仅扩展生成目标画幅空白区域。​
限制/检测客户输入图图片比例(如要求客户输入图片必须为1:1的图片),固定数个扩展的比例(如16:9)​
服务端预置换算逻辑,预置输入图与输出图尺寸转换对应参数​
根据换算逻辑传相应参数​
不传​
​图片​​
1:1原图​
​图片​​
16:9画幅​
四边扩展​
以原图为中心,支持自定义扩展逻辑,比如向上扩展20%,向右扩展60%,向左扩展40%。逻辑也是默认原图大小不变,仅扩展生成自由设置的区域。​
支持用户自定义输入,或者前端/后段固定指定尺寸​
在这里传相应参数​
不传​
​图片​​
原图​
​图片​​
向上扩展20%,向右扩展60%,向左扩展40%​
画布扩展​
可以定义一个画布(可以固定尺寸,也可以支持自定义调节),支持原图在画布里的位置、大小、旋转等交互,最终生成固定画布的扩展图;​
前端有画布,支持用户上传图片后在画布内调整​
不传​
传画布相应信息​
​图片​​
​
​
传top、left方式​
按outpainting智能扩图接口文档传参,计算扩展比例即可​
​
// 请求参数demo​
{​
"req_key": "i2i_outpainting",​
"prompt": "蓝色的海洋",​
"binary_data_base64": ["原图base64"],​
"scale": 7,​
"seed": -1,​
"steps": 30,​
"strength": 0.8,​
"top": 0.1​
"bottom": 0.1,​
"left": 1,​
"right": 1,​
"max_height": 1920,​
"max_width": 1920​
}​
​
​
// 返回参数demo​
{​
"code": 10000,​
"data": {​
"algorithm_base_resp": {​
"status_code": 0,​
"status_message": "Success"​
},​
"binary_data_base64": ["扩展图base64"],​
"request_id": "977202e694283d9e430e6d91d3e0bc6540077ea26de523ba7dfd3856409db693"​
},​
"message": "Success",​
"request_id": "20240314153408C09A6A1651795D028F5A",​
"status": 10000,​
"time_elapsed": "3.9405467s"​
}​
​
使用mask方式​
获取延边后的图​
前端扩展Demo​
  • 修改图片路径:img.src = '女侧119K.JPG';​
  • 修改画布尺寸 :canvas id="canvas" width="1500" height="1500​
  • 延边完成后,右键保存到本地或上传到云端生成http链接​
​
<!DOCTYPE html>​
<html lang="en">​
​
<head>​
<meta charset="UTF-8">​
<title>Interactive Image Manipulation on Canvas</title>​
<style>​
.container {​
text-align: center;​
}​
​
.sidebar {​
text-align: left;​
float: left;​
margin-right: 20px;​
font-family: Arial, sans-serif;​
font-size: 14px;​
width: 150px;​
/* Adjust width as per requirement */​
}​
​
#canvasContainer {​
position: relative;​
display: inline-block;​
margin-bottom: 10px;​
}​
​
#canvas {​
border: 1px solid #000;​
}​
​
.instructions {​
margin-bottom: 10px;​
line-height: 1.5;​
}​
​
.instructions strong {​
color: #333;​
}​
</style>​
</head>​
​
<body>​
​
<div class="container">​
<div id="canvasContainer">​
<div class="sidebar">​
<div class="instructions">​
<strong>操作说明:</strong><br>​
- 拖动: 鼠标左键点击图片并拖动<br>​
- 缩放: 在图片上按住 Alt 键并上下拖动鼠标<br>​
- 旋转: 在图片上按住 Shift 键并左右拖动鼠标<br>​
</div>​
<button id="saveBtn">Save Image</button>​
</div>​
<canvas id="canvas" width="1500" height="1500"></canvas>​
</div>​
</div>​
​
<script>​
var canvas = document.getElementById('canvas');​
var ctx = canvas.getContext('2d');​
var img = new Image();​
var imgLoaded = false;​
var dragging = false, rotating = false, scaling = false;​
var imgX = canvas.width / 2;​
var imgY = canvas.height / 2;​
var imgScale = 1.0;​
var imgRotate = 0;​
var prevX, prevY;​
var ROTATION_SENSITIVITY = 0.5; // You can adjust the sensitivity​
var SCALE_SENSITIVITY = 0.001; // You can adjust the sensitivity​
​
// 图片路径,替换为实际图片路径​
img.src = '女侧119K.JPG';​
​
img.onload = function () {​
imgLoaded = true;​
drawImage();​
}​
​
function drawImage() {​
if (!imgLoaded) return;​
​
ctx.clearRect(0, 0, canvas.width, canvas.height);​
ctx.save();​
ctx.translate(imgX, imgY);​
ctx.rotate(imgRotate * Math.PI / 180);​
ctx.scale(imgScale, imgScale);​
ctx.drawImage(img, -img.width / 2, -img.height / 2);​
ctx.restore();​
}​
​
function getMousePos(evt) {​
var rect = canvas.getBoundingClientRect();​
return {​
x: evt.clientX - rect.left,​
y: evt.clientY - rect.top​
};​
}​
​
canvas.addEventListener('mousedown', function (evt) {​
var pos = getMousePos(evt);​
prevX = pos.x;​
prevY = pos.y;​
scaling = evt.altKey;​
rotating = evt.shiftKey;​
dragging = !(scaling || rotating);​
});​
​
canvas.addEventListener('mousemove', function (evt) {​
if (!dragging && !rotating && !scaling) return;​
​
var pos = getMousePos(evt);​
var dx = pos.x - prevX;​
var dy = pos.y - prevY;​
prevX = pos.x;​
prevY = pos.y;​
​
if (dragging) {​
imgX += dx;​
imgY += dy;​
} else if (rotating) {​
imgRotate += dx * ROTATION_SENSITIVITY;​
} else if (scaling) {​
var scaleFactor = 1 + dy * SCALE_SENSITIVITY;​
imgScale *= scaleFactor;​
}​
drawImage();​
});​
​
canvas.addEventListener('mouseup', function () {​
dragging = false;​
rotating = false;​
scaling = false;​
});​
​
canvas.addEventListener('mouseleave', function () {​
dragging = false;​
rotating = false;​
scaling = false;​
});​
​
document.getElementById('saveBtn').addEventListener('click', function () {​
var dataURL = canvas.toDataURL('image/png');​
var link = document.createElement('a');​
link.download = '延边图.png';​
link.href = dataURL;​
link.click();​
});​
</script>​
​
</body>​
​
</html>​
​
获取黑白mask​
Go 获取mask demo​
​
package main​
​
import (​
"image"​
"image/color"​
​
// 确保导入了支持的图像格式包​
_ "image/jpeg"​
"image/png"​
"os"​
​
"github.com/cloudwego/hertz/pkg/common/hlog"​
)​
​
// FileUrl2Image 从指定链接获取图片并转成Image​
func FileUrl2Image(fileUrl string) image.Image {​
// 发送get请求,拉取文件​
resp, err := http.Get(fileUrl)​
if err != nil {​
hlog.Errorf("+++++HTTP GET error: %w", err)​
return nil​
}​
defer resp.Body.Close()​
// 检查HTTP响应状态码​
if resp.StatusCode != http.StatusOK {​
hlog.Errorf("+++++HTTP Status not OK: %d", resp.StatusCode)​
return nil​
}​
img, _, err := image.Decode(resp.Body)​
if err != nil {​
hlog.Error("+++++解码图片失败, img:", img)​
hlog.Error("+++++解码图片失败, err:", err)​
}​
return img​
}​
​
​
// CreateMaskBasedOnAlpha 将使用传入图片的alpha通道值来创建一个遮罩​
func createMaskBasedOnAlpha(img image.Image) image.Image {​
// 获取原始图片的边界​
rect := img.Bounds()​
// 创建一个相同大小的mask图像(默认遮罩是全黑的,也就是透明的)​
mask := image.NewAlpha(rect)​
​
// 遍历原始图片的每个像素​
for y := rect.Min.Y; y < rect.Max.Y; y++ {​
for x := rect.Min.X; x < rect.Max.X; x++ {​
// 取出原始图片的像素​
_, _, _, a := img.At(x, y).RGBA()​
// RGBA方法返回的颜色值范围是0-65535​
// 将其转换为0-255范围,以便与通常的alpha值比较。​
alpha := uint8(a >> 8)​
​
// 基于alpha值判断是否应该在遮罩中保留这个像素​
if alpha > 128 { // 假设alpha大于128我们才保留这个像素​
mask.SetAlpha(x, y, color.Alpha{A: 0}) // 不透明​
} else {​
mask.SetAlpha(x, y, color.Alpha{A: 255}) // 完全透明​
}​
}​
}​
return mask​
}​
​
// saveMaskToFile 将遮罩图像保存到文件中​
// mask: 遮罩信息​
// maskSavePath: mask转成图片的路径​
func saveMaskToFile(mask image.Image, maskSavePath string) error {​
// 创建一个文件用于保存遮罩​
ofi, err := os.OpenFile(maskSavePath, os.O_RDWR|os.O_CREATE, os.ModePerm)​
if err != nil {​
return err​
}​
defer ofi.Close()​
​
// 使用png格式将遮罩编码并写入文件​
err = png.Encode(ofi, mask)​
if err != nil {​
return err​
}​
​
return nil​
}​
​
func GenerateMask(img image.Image, maskPath string) {​
// 从文件中解码图片​
err := saveMaskToFile(createMaskBasedOnAlpha(img), maskPath)​
if err != nil {​
hlog.Error("+++++保存mask失败,err:", err)​
}​
}​
​
func main() {​
imgUrl := "https://xxxx" // 更换成第一步涂抹完成的图片链接​
GenerateMask(FileUrl2Image(imgUrl), "test-mask.png") // 此时生成的test-mask.png即为接口调用使用的mask​
}​
​
使用延边图及其mask,调用接口​
完成3.1、3.2步骤后,开始调用智能扩图outpainting接口​
https://www.volcengine.com/docs/6791/1223722​
​
// 请求参数Demo​
{​
"req_key": "i2i_outpainting",​
"prompt": "蓝色的海洋",​
"binary_data_base64": ["延边图base64", "延边图mask"],​
"scale": 7,​
"seed": -1,​
"steps": 30,​
"strength": 0.8​
"max_height": 1920,​
"max_width": 1920,​
}​
​
​
// 返回参数Demo​
{​
"code": 10000,​
"data": {​
"algorithm_base_resp": {​
"status_code": 0,​
"status_message": "Success"​
},​
"binary_data_base64": ["扩展图base64"],​
"request_id": "b412f34efa44b30c039654f3dfafd135795a8eb4a374cf8d9a1548bf01b27543"​
},​
"message": "Success",​
"request_id": "20240314160151EF84082D2A4970000712",​
"status": 10000,​
"time_elapsed": "4.495273592s"​
}​
​
​
最近更新时间:2025.09.03 15:56:08
这个页面对您有帮助吗?
有用
有用
无用
无用