成都h5模板建站,wordpress食谱,铜山网站开发,网站集约化建设的通知本文作者为 360 奇舞团前端开发工程师 随着AI的火热发展#xff0c;涌现了一些AI模特换装的前端工具#xff08;比如weshop网站#xff09;#xff0c;他们是怎么实现的呢#xff1f;使用了什么技术呢#xff1f;下文我们就来探索一下其实现原理。 总体的实现流程如下涌现了一些AI模特换装的前端工具比如weshop网站他们是怎么实现的呢使用了什么技术呢下文我们就来探索一下其实现原理。 总体的实现流程如下我们将下图中的这个模特的图片使用Segment Anything Model在后端分割图层然后将分割后的图层mask信息返回给前端处理。在前端中选择需要保留的图层信息如下图中的模特的衣服图层然后将选中的图层信息交给后端中的Stable Diffusion处理。后端使用原始图片结合选中的图层蒙版图片结合图生图的功能可以实现weshop等网站的模特换衣等功能。 本文先简单介绍一下使用SAM智能图层分割然后主要介绍一下在前端中怎么对分割后的图层进行选择的处理流程。 使用SAM识别图层 首先我们需要对图层进行分割在SAM出来之前我们需要使用PS将模特的衣服选取出来然后倒出衣服的模板然后再使用其他工具进行替换。但是现在有了SAM后我们可以对图片中的事物进去只能区分获取各种物品的图层。 Segment Anything ModelSAM是一种尖端的图像分割模型可以进行快速分割为图像分析任务提供无与伦比的多功能性。SAM 的先进设计使其能够在无需先验知识的情况下适应新的图像分布和任务这一功能称为零样本传输。SAM 使任何人都可以在不依赖标记数据的情况下为其数据创建分段掩码。 要深入了解 Segment Anything 模型和 SA-1B 数据集请访问Segment Anything 网站https://segment-anything.com/并查看研究论文Segment Anythinghttps://arxiv.org/abs/2304.02643。 我们使用SAM进行图像分割将一个图片中的物体分割成不同的部分。 def mask2rle(img):img: numpy array, 1 - mask, 0 - backgroundReturns run length as string formatedpixels img.T.flatten()pixels np.concatenate([[0], pixels, [0]])runs np.where(pixels[1:] ! pixels[:-1])[0] 1runs[1::2] - runs[::2]return .join(str(x) for x in runs)def trans_anns(anns):if len(anns) 0:returnsorted_anns sorted(anns, key(lambda x: x[area]), reverseFalse)list []index 0# 对每个注释进行处理for ann in sorted_anns:bool_array ann[segmentation]# 将boolean类型的数组转换为int类型int_array bool_array.astype(int)# 转化为RLE格式rle mask2rle(int_array)list.append({index: index, mask: rle})index 1return listimage cv2.imread(your image path)import sys
sys.path.append(your segment-anything link path)
from segment_anything import sam_model_registry, SamAutomaticMaskGenerator, SamPredictor# sam 模型路径
sam_checkpoint your sam model path
# 根据下载的模型设置对应的类型
model_type vit_h# device cuda
sam sam_model_registry[model_type](checkpointsam_checkpoint)
# sam.to(devicedevice)
mask_generator SamAutomaticMaskGenerator(sam)
masks mask_generator.generate(image)
# 处理sam返回的图层信息
mask_list trans_anns(masks)mask_obj {height: image.shape[0],width: image.shape[1],mask_list: mask_list
}import json
print(json.dumps(mask_obj)) 运行以上python代码之前需要配置sam的python环境具体的配置描述请查看sam的官方描述。 我们通过以上代码将我们提供的图片通过SAM处理后返回图层分割数据。在trans_anns方法中将图层按照area从小到大的顺序排序。遍历各个图层将boolean类型的数组转换为 0 1 int类型然后对二维numpy array类型的0 1二进制mask图像转换为RLE格式。 RLE是一种简单的无损数据压缩算法通常用于表示连续的相同值的序列。RLE编码的字符串通常用于在图像分割等任务中存储和传输二进制掩码信息以便更有效地表示图像中的目标区域。并且方便数据压缩和传输。我们参照的这种编解码方式。也可以使用coco RLE的编解码方式。 将编码后的各图层信息存储到list中就可以通过接口传输给前端处理了。 前端选择图层 下面这些是本文的重点在前端将刚才解析后的mask_list信息展示并可以通过交互选取需要保留的模版并生成最终合并选取的mask生成一个需要保留的服装模版。 body中的基本组件为 div idlayer-box style width: 500px; height: 500px;position: relativeimg stylewidth: 100%; height: 100%; position: absolute srchttps://p0.ssl.qhimg.com/t01989f0d446bed3e58.jpg //divdiv idsave clicksave stylemargin-top: 20px;margin-right: 20px; margin-left: 20px;保存/divcanvas idmergedCanvas styleborder:1px solid #000;/canvas id为layer-box的div组件作为各个mask的父组件用于查找和管理各个mask的隐藏和展示。其子组件中的第一个标签是展示原始的模特图片的。 id为save的组件在点击时可以处理保存选中的各个mask为一个新的mask图片用于处理图片合成。 id为mergedCanvas的canvas是进行图片合成和展示合成后的图片的。 解析SAM处理后的mask_list信息 /*** rle格式图片信息转换为mask信息*/function rle2mask(mask_rle, shape [500, 500]) {/*mask_rle: run-length as string formatted (start length)shape: [width, height] of array to returnReturns an array, 1 - mask, 0 - background*/const s mask_rle.split( );let starts s.filter((_, index) index % 2 0).map(Number);const lengths s.filter((_, index) index % 2 ! 0).map(Number);starts starts.map(start start - 1);const ends starts.map((start, index) start lengths[index]);const img new Array(shape[0] * shape[1]).fill(0);for (let i 0; i starts.length; i) {for (let j starts[i]; j ends[i]; j) {img[j] 1;}}// return transposeArray(img, shape);const transposed new Array(shape[1]).fill(0).map(() new Array(shape[0]).fill(0));for (let i 0; i shape[0]; i) {for (let j 0; j shape[1]; j) {transposed[j][i] img[i * shape[1] j];}}return transposed;}/*** 转换mask图片信息并设置mask的填充颜色*/function transformMaskImage(item, _width, _height) {let canvas document.createElement(canvas);let canvasContext canvas.getContext(2d);canvas.width _width;canvas.height _height;let rgbaData rle2mask(item.mask || , [_width, _height])for (let y 0; y rgbaData.length; y) {let row rgbaData[y];for (let x 0; x row.length; x) {let dot rgbaData[y][x];if (1 dot canvasContext) {// 值为1的点填充颜色(canvasContext.fillStyle #4169eb), canvasContext.fillRect(x, y, 1, 1);}}}// canvas当前层的图片base64格式// matrix上边生成的二维数组return { imageData: canvas.toDataURL(image/png), matrix: rgbaData };}// 使用sam处理后的图层信息rle编码后的由于篇幅限制已省略const res { height: 500, width: 500, mask_list: [{ index: 0, mask: 109864 3 110361 7 110860 9 111359 10 111859 10 112359 10 112860 9 113360 10 113860 10 114360 10 114860 10 115360 10 115861 8 }, { index: 1, mask: 121910 2 122409 4 122908 6 123408 7 123907 8 124407 9 124907 9 125406 11 125905 12 126404 13 126905 12 127405 12 127906 12 128406 12 128907 11 129407 10 129908 8 130408 4 },......] }layers res.mask_list.map((item) transformMaskImage(item, res.width, res.height)); res是sam处理后返回的图层信息由于篇幅限制已省略详情请看demohttps://github.com/yuhao1128/AI-model-mask-select-demo/blob/main/index.html中的数据。遍历mask_list使用canvas保存各个mask的信息。由于前面sam处理后的mask_list是经过压缩编码的所以在rle2mask方法中对rle编码后的数据解码为 0/1二维数组的格式。rle2mask中的解码方式请参考这种解码https://www.kaggle.com/code/pestipeti/decoding-rle-masks方式。 然后遍历二维数组将值为1的点填充颜色此处是填充的rgba为#4169eb的颜色可以根据需要自己修改为其他的颜色。此处填充的颜色会在下文中鼠标移动到mask上面时在mask展示的时候呈现此颜色。 最后在layers中存储各个mask的base64格式的图片信息和二维数组信息。 将各个mask添加到图层 const box document.querySelector(#layer-box);const baseStyle width:100%;height:100%;position: absolute;;//将各个mask添加为layer-box的子组件并隐藏mask的展示layers.forEach((ele) {const image document.createElement(img);image.src ele.imageData;image.style ${baseStyle}opacity:0;image.className layer;box.append(image);}); 将各个mask添加的图片添加为layer-box组件的子组件并且设置opacity为0先隐藏这些mask的展示在下文会监听鼠标的位置通过设置mask的opacity属性来展示mask。 监听鼠标的位置和点击 // 鼠标移入mask组件的区域时展示maskbox.addEventListener(mousemove, (e) {const { clientX, clientY } e;const X box.getBoundingClientRect().left document.body.scrollLeft;const Y box.getBoundingClientRect().top document.body.scrollTop;const x parseInt(res.width * (clientX - X) / box.getBoundingClientRect().width)const y parseInt(res.height * (clientY - Y) / box.getBoundingClientRect().height)const allLayers box.querySelectorAll(.layer);const index layers.findIndex((item) item.matrix?.[y]?.[x]);allLayers.forEach((ele, i) {if (i index) {ele.style ${baseStyle}opacity:0.7;} else {// 已经选中的不需要隐藏if (selectedIndexList.indexOf(i) -1) {ele.style ${baseStyle}opacity:0;}}});});// 鼠标移出mask组件的区域时隐藏maskbox.addEventListener(mouseout, (e) {console.log(mouseout selectedIndexList, selectedIndexList);const allLayers box.querySelectorAll(.layer);allLayers.forEach((ele, i) {// 只有选中的才会展示if (selectedIndexList.indexOf(i) -1) {ele.style ${baseStyle}opacity:0.7;} else {ele.style ${baseStyle}opacity:0;}});});// 用户点击时保存用户选中的mask的indexbox.addEventListener(mousedown, (e) {const { clientX, clientY } e;const X box.getBoundingClientRect().left document.body.scrollLeft;const Y box.getBoundingClientRect().top document.body.scrollTop;const x parseInt(res.width * (clientX - X) / box.getBoundingClientRect().width)const y parseInt(res.height * (clientY - Y) / box.getBoundingClientRect().height)const index layers.findIndex((item) item.matrix?.[y]?.[x]);if (selectedIndexList.indexOf(index) -1) {//保存点击选中的元素indexselectedIndexList.push(index)}}); box就是上文的layer-box是各个mask的父组件。layer-box监听鼠标的move事件和click事件当move到对应的mask上时将mask展示移除mask时隐藏mask。mask在list中是从小到大的顺序所以遍历匹配mask时会优先匹配面积小的组件方便灵活选择。当点击mask的位置时保存mask在list中的index到selectedIndexList中方便后续导出保存选择并高亮展示选中的mask。 选中的mask合成图片 // 存储各个图层图片信息let layers []// 选择layer的indexconst selectedIndexList []// 点击保存document.getElementById(save).onclick function () {const images [];selectedIndexList.forEach(index {images.push(layers[index].imageData)})drawing(images)}/*** 图片合成*/function drawing(images) {const canvas document.getElementById(mergedCanvas);canvas.width 500; // 设置canvas宽canvas.height 500; // 设置canvas高const ctx canvas.getContext(2d);let loadedImages 0;images.forEach(function (src) {const img new Image();img.src src;img.onload function () {loadedImages;// 绘制每张图片到 canvas 上ctx.drawImage(img, 0, 0);// 如果所有图片都加载完成保存合并后的图片if (loadedImages images.length) {// 获取图片的像素数据const imageData ctx.getImageData(0, 0, img.width, img.height);const data imageData.data;// 转换为黑白效果for (let i 0; i data.length; i 4) {// 将 R、G、B 设置为0data[i] 0;data[i 1] 0;data[i 2] 0;}// 将修改后的数据放回 canvasctx.putImageData(imageData, 0, 0);// 导出为 base64 图片const mergedImageBase64 canvas.toDataURL(image/png);// 如果需要你可以将mergedImageBase64图片用于其他操作比如发送到服务器}};});} 当选择完成后可以点击“保存”按钮将选择的mask使用canvas生成一个合并后的图片。此处已将合成后的图片转换为黑白蒙版照片之后可以使用这个合并后的图片进行后续的处理。 根据选中的图层点击保存后生成的模板如下图所示。 预览效果https://yuhao1128.github.io/AI-model-mask-select-demo/、代码详情https://github.com/yuhao1128/AI-model-mask-select-demo/blob/main/index.html 使用Stable Diffusion进行后续的处理 由于篇幅的限制并且这部分网络上以及有很多的介绍资料就不再本文中进行介绍了可以参考这篇文章https://www.uisdc.com/stable-diffusion-24的介绍尝试体验一下在本地中使用Stable Diffusion的图生图的「重绘蒙版」来进行模特的重新绘制。 也可以在后端部署Stable Diffusion服务中处理模特换装。将前面的模特原图以及生成的蒙版图片以及其他的SD的图生图功能的参数传给后端的SD服务处理。 除了模特换装的功能上面的流程还可以应用到物品换背景的功能中。其他的一些智能抠图智能替换的功能都可以扩展上面的处理流程来实现。 参考链接 https://github.com/facebookresearch/segment-anything https://juejin.cn/post/7248903246970503223#heading-2 https://www.uisdc.com/stable-diffusion-24 - END - 关于奇舞团 奇舞团是 360 集团最大的大前端团队代表集团参与 W3C 和 ECMA 会员TC39工作。奇舞团非常重视人才培养有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。
文章转载自: http://www.morning.lgmty.cn.gov.cn.lgmty.cn http://www.morning.pdtjj.cn.gov.cn.pdtjj.cn http://www.morning.rnds.cn.gov.cn.rnds.cn http://www.morning.wmsgt.cn.gov.cn.wmsgt.cn http://www.morning.rtzd.cn.gov.cn.rtzd.cn http://www.morning.gxtbn.cn.gov.cn.gxtbn.cn http://www.morning.qwmpn.cn.gov.cn.qwmpn.cn http://www.morning.qbfwb.cn.gov.cn.qbfwb.cn http://www.morning.zsfooo.com.gov.cn.zsfooo.com http://www.morning.thrgp.cn.gov.cn.thrgp.cn http://www.morning.nlqmp.cn.gov.cn.nlqmp.cn http://www.morning.qxlxs.cn.gov.cn.qxlxs.cn http://www.morning.pgrsf.cn.gov.cn.pgrsf.cn http://www.morning.jrrqs.cn.gov.cn.jrrqs.cn http://www.morning.qxgmp.cn.gov.cn.qxgmp.cn http://www.morning.czqqy.cn.gov.cn.czqqy.cn http://www.morning.gnfkl.cn.gov.cn.gnfkl.cn http://www.morning.qqtzn.cn.gov.cn.qqtzn.cn http://www.morning.znkls.cn.gov.cn.znkls.cn http://www.morning.wkgyz.cn.gov.cn.wkgyz.cn http://www.morning.ljxxl.cn.gov.cn.ljxxl.cn http://www.morning.kxrhj.cn.gov.cn.kxrhj.cn http://www.morning.gtmgl.cn.gov.cn.gtmgl.cn http://www.morning.dygsz.cn.gov.cn.dygsz.cn http://www.morning.rmqmc.cn.gov.cn.rmqmc.cn http://www.morning.rntyn.cn.gov.cn.rntyn.cn http://www.morning.hhskr.cn.gov.cn.hhskr.cn http://www.morning.pzlcd.cn.gov.cn.pzlcd.cn http://www.morning.qtkdn.cn.gov.cn.qtkdn.cn http://www.morning.fphbz.cn.gov.cn.fphbz.cn http://www.morning.bxhch.cn.gov.cn.bxhch.cn http://www.morning.rqhbt.cn.gov.cn.rqhbt.cn http://www.morning.bkpbm.cn.gov.cn.bkpbm.cn http://www.morning.grzpc.cn.gov.cn.grzpc.cn http://www.morning.ngjpt.cn.gov.cn.ngjpt.cn http://www.morning.fwnqq.cn.gov.cn.fwnqq.cn http://www.morning.qjrjs.cn.gov.cn.qjrjs.cn http://www.morning.ryztl.cn.gov.cn.ryztl.cn http://www.morning.jntdf.cn.gov.cn.jntdf.cn http://www.morning.rrbhy.cn.gov.cn.rrbhy.cn http://www.morning.zlkps.cn.gov.cn.zlkps.cn http://www.morning.rfwqt.cn.gov.cn.rfwqt.cn http://www.morning.gwmny.cn.gov.cn.gwmny.cn http://www.morning.dnmwl.cn.gov.cn.dnmwl.cn http://www.morning.dhnqt.cn.gov.cn.dhnqt.cn http://www.morning.fjzlh.cn.gov.cn.fjzlh.cn http://www.morning.fhqsm.cn.gov.cn.fhqsm.cn http://www.morning.gassnw.com.gov.cn.gassnw.com http://www.morning.jfxdy.cn.gov.cn.jfxdy.cn http://www.morning.jthjr.cn.gov.cn.jthjr.cn http://www.morning.yqwrj.cn.gov.cn.yqwrj.cn http://www.morning.srmdr.cn.gov.cn.srmdr.cn http://www.morning.smsjx.cn.gov.cn.smsjx.cn http://www.morning.wsrcy.cn.gov.cn.wsrcy.cn http://www.morning.bscsp.cn.gov.cn.bscsp.cn http://www.morning.kmbgl.cn.gov.cn.kmbgl.cn http://www.morning.rzdzb.cn.gov.cn.rzdzb.cn http://www.morning.bpmdn.cn.gov.cn.bpmdn.cn http://www.morning.mytmx.cn.gov.cn.mytmx.cn http://www.morning.tgyzk.cn.gov.cn.tgyzk.cn http://www.morning.kzbpx.cn.gov.cn.kzbpx.cn http://www.morning.supera.com.cn.gov.cn.supera.com.cn http://www.morning.pcrzf.cn.gov.cn.pcrzf.cn http://www.morning.mgtmm.cn.gov.cn.mgtmm.cn http://www.morning.sgnjg.cn.gov.cn.sgnjg.cn http://www.morning.gqfks.cn.gov.cn.gqfks.cn http://www.morning.bpmtj.cn.gov.cn.bpmtj.cn http://www.morning.qhtlq.cn.gov.cn.qhtlq.cn http://www.morning.xcbnc.cn.gov.cn.xcbnc.cn http://www.morning.mwrxz.cn.gov.cn.mwrxz.cn http://www.morning.nptls.cn.gov.cn.nptls.cn http://www.morning.dgfpp.cn.gov.cn.dgfpp.cn http://www.morning.pmftz.cn.gov.cn.pmftz.cn http://www.morning.byywt.cn.gov.cn.byywt.cn http://www.morning.zlqyj.cn.gov.cn.zlqyj.cn http://www.morning.wxlzr.cn.gov.cn.wxlzr.cn http://www.morning.zcqgf.cn.gov.cn.zcqgf.cn http://www.morning.xznrk.cn.gov.cn.xznrk.cn http://www.morning.zcckq.cn.gov.cn.zcckq.cn http://www.morning.dbhnx.cn.gov.cn.dbhnx.cn