AI使用 G-API 实现面部美化算法

news2024/11/25 12:24:12

介绍
在本教程中,您将学习:

示例面部美化算法的基础知识;
如何使用 G-API 推断管道内的不同网络;
如何在视频流上运行 G-API 管道。
先决条件
此示例需要:

装有 GNU/Linux 或 Microsoft Windows 的 PC(支持 Apple macOS,但未经过测试);
OpenCV 4.2 或更高版本使用英特尔®发行版 OpenVINO™ 工具套件构建(使用英特尔® TBB 构建者优先);
OpenVINO™ Toolkit Open Model Zoo 中的以下拓扑:
face-detection-adas-0001;
facial-landmarks-35-adas-0002.
美颜算法
我们将使用现代深度学习技术和传统计算机视觉的组合来实现一个简单的面部美化算法。该算法背后的总体思想是使面部皮肤更光滑,同时保留眼睛或嘴巴对比度等面部特征。该算法使用 DNN 推理识别面部的某些部分,对找到的部分应用不同的过滤器,然后使用基本图像算法将其组合到最终结果中:

简而言之,该算法描述如下:

输入图像\(I\)被传递到非锐化掩模和双边滤波器(分别为\(U\)和\(L\));
输入图像\(I\)被传递到基于SSD的人脸检测器;
SSD 结果(\([1 \times 1 \times 200 \times 7]\) blob)被解析并转换为人脸数组;
每张脸都被传递到地标检测器;
根据为每张脸找到的地标,生成三个图像蒙版:
背景蒙版 \(b\) – 指示原始图像中的哪些区域保持原样;
面部部分蒙版 \(p\) – 识别要保留(锐化)的区域。
面膜 \(s\) – 识别要模糊的区域;
最终结果 \(O\) 是上述特征的组合,计算为 \(O = bI + pU + s*L\)。
基于一组有限的特征(每张脸只有 35 个,包括其所有部分)生成面部元素蒙版并不是一件容易的事,将在以下各节中介绍。

构建 G-API 管道
声明深度学习拓扑
此示例使用两个 DNN 检测器。每个网络接受一个输入并产生一个输出。在 G-API 中,网络是用宏 G_API_NET() 定义的:

G_API_NET(FaceDetector, <cv::GMat(cv::GMat)>, “face_detector”);
G_API_NET(LandmDetector, <cv::GMat(cv::GMat)>, “landm_detector”);
若要获取详细信息,请参阅“人脸分析管道”教程中所述的声明深度学习拓扑。

描述处理图
下面的代码为上述算法生成了一个图形:

cv::GComputation pipeline([=]()
{
    cv::GMat gimgIn;//输入
    cv::GMat faceOut = cv::gapi::infer<custom::FaceDetector>(gimgIn);

GArrayROI garRects = custom::GFacePostProc::on(faceOut, gimgIn, config::kConfThresh);// 后处理
cv::GArray<cv::GMat> landmOut = cv::gapi::infer<custom::LandmDetector>(garRects, gimgIn);
cv::GArray<地标> garElems;// |
cv::GArray garJaws;// |输出数组
std::tie(garElems, garJaws) = custom::GLandmPostProc::on(landmOut, garRects);// 后处理
cv::GArray garElsConts;脸部元素 //
cv::GArray garFaceConts;整张脸 //
std::tie(garElsConts, garFaceConts) = custom::GGetContours::on(garElems, garJaws);//插值
cv::GMat mskSharp = custom::GFillPolyGContours::on(gimgIn, garElsConts);// |
cv::GMat mskSharpG = cv::gapi::gaussianBlur(mskSharp, config::kGKernelSize, // |
config::kGSigma);// |
cv::GMat mskBlur = custom::GFillPolyGContours::on(gimgIn, garFaceConts);// |
cv::GMat mskBlurG = cv::gapi::gaussianBlur(mskBlur, config::kGKernelSize, // |
config::kGSigma);// |绘制蒙版
mask() 中的第一个参数是 Blur,因为我们想从 // |
BlurG 下一步: // |
cv::GMat mskBlurFinal = mskBlurG - cv::gapi::mask(mskBlurG, mskSharpG);// |
cv::GMat mskFacesGaussed = mskBlurFinal + mskSharpG;// |
cv::GMat mskFacesWhite = cv::gapi::threshold(mskFacesGaussed, 0, 255, cv::THRESH_BINARY);// |
cv::GMat mskNoFaces = cv::gapi::bitwise_not(mskFacesWhite);// |
cv::GMat gimgBilat = custom::GBilatFilter::on(gimgIn, config::kBSize,
config::kBSigmaCol, config::kBSigmaSp);
cv::GMat gimgSharp = custom::unsharpMask(gimgIn, config::kUnshSigma,
config::kUnshStrength);
敷面膜
应该使用自定义函数 mask3C() 而不仅仅是 gapi::mask()
由于 mask() 仅提供CV_8UC1源代码(我们有CV_8U3C)
cv::GMat gimgBilatMasked = custom::mask3C(gimgBilat, mskBlurFinal);
cv::GMat gimgSharpMasked = 自定义::mask3C(gimgSharp, mskSharpG);
cv::GMat gimgInMasked = 自定义::mask3C(gimgIn, mskNoFaces);
cv::GMat gimgBeautif = gimgBilatMasked + gimgSharpMasked + gimgInMasked;
返回 cv::GComputation(cv::GIn(gimgIn), cv::GOut(gimgBeautif,
cv::gapi::copy(gimgIn),
garFaceConts,
加埃尔斯康茨,
garRects));
});
生成的图形是 G-API 的标准操作、用户定义的操作(命名空间)和 DNN 推理的混合体。通用函数允许在管道内触发推理;要推断的网络被指定为模板参数。示例代码使用两个版本:custom::cv::gapi::infer<>()cv::gapi::infer<>()

面向帧的用于检测输入帧上的人脸。
面向 ROI 列表的版本用于对人脸列表进行地标推断——此版本为每张人脸生成一系列地标。
有关此内容的详细信息,请参阅“人脸分析管道”(生成 GComputation 部分)。

G-API 中的 Unsharp 蒙版
图像 I 的非锐化掩码 U 定义为:U我
在这里插入图片描述
其中 M() 是中值滤波器,L() 是拉普拉斯算子,s 是强度系数。虽然 G-API 没有提供开箱即用的此函数,但它通过现有的 G-API 操作自然地表达:M()L()s
内联 cv::GMat custom::unsharpMask(const cv::GMat &src,
常量国际西格玛,
常量浮动强度)
{
cv::GMat blurred = cv::gapi::medianBlur(src, sigma);
cv::GMat 拉普拉斯语 = 自定义::GLaplacian::on(模糊,CV_8U);
返回 (src - (拉普拉斯 * 强度));
}
请注意,上面截取的代码是使用 G-API 类型定义的常规 C++ 函数。用户可以编写这样的函数来简化图形构造;调用时,此函数只是将相关节点放入使用它的管道中。

自定义操作
人脸美化图正在广泛使用自定义操作。本章重点介绍最有趣的内核,有关在 G-API 中定义操作和实现内核的一般信息,请参阅 G-API 内核 API。

人脸检测器后处理
人脸检测器输出将转换为具有以下内核的人脸数组:

用矢量ROI = std::vector<cv::Rect>;
GAPI_OCV_KERNEL(GCPUFacePostProc、GFacePostProc)
{
静态 void run(const cv::Mat &inDetectResult,
const cv::Mat &inFrame,
const float faceConfThreshold,
VectorROI &outFaces)
{
常量 int kObjectSize = 7;
const int imgCols = inFrame。size().width;
const int imgRows = inFrame。size().高度;
const cv::Rect borders({0, 0}, inFrame.大小());
outFaces.clear();
const int numOfDetections = inDetectResult。尺寸[2];
const float *data = inDetectResult。ptr<浮点>();
for (int i = 0; i < numOfDetections; i++)
{
常量浮点数 faceId = data[i * kObjectSize + 0];
if (faceId < 0.f) // 表示检测结束
{
破;
}
常量浮点数 faceConfidence = data[i * kObjectSize + 2];
我们可以通过“conf”字段来减少检测
以避免探测器的错误。
if (faceConfidence > faceConfThreshold)
{
const float left = data[i * kObjectSize + 3];
const float top = data[i * kObjectSize + 4];
const float right = data[i * kObjectSize + 5];
const float bottom = data[i * kObjectSize + 6];
这些是归一化坐标,介于 0 和 1 之间;
要获得真实的像素坐标,我们应该将其乘以
图像大小分别为以下方向:
cv:😛 oint tl(toIntRounded(left * imgCols),
toIntRounded(顶部 * imgRows));
cv:😛 oint br(toIntRounded(right * imgCols),
toIntRounded(底部 * imgRows));
outFaces.push_back(cv::Rect(tl, br) & borders);
}
}
}
};
面部特征点后期处理
该算法使用OpenVINO™ Open Model Zoo的通用面部特征检测器(细节)推断面部元素(如眼睛、嘴巴和头部轮廓本身)的位置。但是,按原样检测到的地标不足以生成蒙版 - 此操作需要由闭合轮廓表示的面部感兴趣区域,因此应用一些插值来获取它们。此地标处理和插值由以下内核执行:

GAPI_OCV_KERNEL(GCPUGetContours、GGetContours)
{
static void run(const std::vector &vctPtsFaceElems, // 18个面部元素的地标
const std::vector &vctCntJaw, // 17个下颌标志
std::vector &vctElems轮廓,
std::vector &vctFaceContours)
{
size_t numFaces = vctCntJaw.size();
CV_Assert(numFaces == vctPtsFaceElems.size());
CV_Assert(vctElemsContours.size() == 0ul);
CV_Assert(vctFaceContours.size() == 0ul);
vctFaceElemsContours 将存储找到的所有面部元素的轮廓
在输入图像中,即每个检测到的人脸有 4 个元素(两只眼睛、鼻子、嘴巴):
vctElemsContours.reserve(numFaces * 4);
vctFaceElemsContours 将存储输入图像中的所有面部轮廓:
vctFaceContours.reserve(numFaces);
轮廓 cntFace、cntLeftEye、cntRightEye、cntNose、cntMouth;
cntNose.reserve(4);
for (size_t i = 0ul; i < numFaces; i++)
{
面部元素轮廓
左眼:
用半椭圆(使用眼点)近似下眼轮廓并存储在 cntLeftEye 中:
cntLeftEye = getEyeEllipse(vctPtsFaceElems[i][1], vctPtsFaceElems[i][0]);
顺时针推动左眉毛:
cntLeftEye.insert(cntLeftEye.end(), {vctPtsFaceElems[i][12], vctPtsFaceElems[i][13],
vctPtsFaceElems[i][14]});
右眼:
将下眼轮廓近似为半椭圆(使用眼点)并存储在 vctRightEye 中:
cntRightEye = getEyeEllipse(vctPtsFaceElems[i][2], vctPtsFaceElems[i][3]);
顺时针推动右眉毛:
cntRightEye.insert(cntRightEye.end(), {vctPtsFaceElems[i][15], vctPtsFaceElems[i][16],
vctPtsFaceElems[i][17]});
鼻子:
顺时针存放鼻子点
cntNose.clear();
cntNose.insert(cntNose.end(), {vctPtsFaceElems[i][4], vctPtsFaceElems[i][7],
vctPtsFaceElems[i][5], vctPtsFaceElems[i][6]});
一张嘴:
用两个半椭圆(使用嘴尖)近似嘴轮廓并存储在 vctMouth 中:
cntMouth = getPatchedEllipse(vctPtsFaceElems[i][8], vctPtsFaceElems[i][9],
vctPtsFaceElems[i][10], vctPtsFaceElems[i][11]);
将所有元素存储在向量中:
vctElemsContours.insert(vctElemsContours.end(), {cntLeftEye, cntRightEye, cntNose, cntMouth});
脸部轮廓:
用半椭圆(使用下颌点)近似额头轮廓并存储在 vctFace 中:
cntFace = getForeheadEllipse(vctCntJaw[i][0], vctCntJaw[i][16], vctCntJaw[i][8]);
椭圆是顺时针绘制的,但颌骨轮廓点反之亦然,因此有必要推动
cntJaw 从结束到开始使用反向迭代器:
std::copy(vctCntJaw[i].crbegin(), vctCntJaw[i].crend(), std::back_inserter(cntFace));
将面部轮廓存储在另一个矢量中:
vctFaceContours.push_back(cntFace);
}
}
};
内核采用两个非规范化地标坐标数组,并返回元素的闭合轮廓数组和面的闭合轮廓数组;换言之,输出是第一个要锐化的图像区域轮廓阵列,第二个是另一个要平滑的图像区域轮廓。

这里和下面是一个点的向量。Contour

获得眼部轮廓
眼部轮廓通过以下功能进行估计:

内联 int custom::getLineInclinationAngleDegrees(const cv:😛 oint &ptLeft, const cv:😛 oint &ptRight)
{
const cv:😛 oint 残差 = ptRight - ptLeft;
如果 (残差。y == 0 && 残差。x == 0)
返回 0;

返回 toIntRounded(atan2(toDouble(residual.y)、toDouble(残差。x)) * 180.0 / CV_PI);
}
内嵌轮廓自定义::getEyeEllipse(const cv:😛 oint &ptLeft, const cv:😛 oint &ptRight)
{
轮廓 cntEyeBottom;
const cv:😛 oint ptEyeCenter((ptRight + ptLeft) / 2);
常量 int angle = getLineInclinationAngleDegrees(ptLeft, ptRight);
const int axisX = toIntRounded(cv::norm(ptRight - ptLeft) / 2.0);
根据研究,平均而言,眼睛的 Y 轴约为
X 的 1/3。
常量 int axisY = axisX / 3;
我们需要椭圆的下半部分:
静态 constexpr int kAngEyeStart = 0;
静态 constexpr int kAngEyeEnd = 180;
cv::ellipse2Poly(ptEyeCenter, cv::Size(axisX, axisY), angle, kAngEyeStart, kAngEyeEnd, config::kAngDelta,
cntEyeBottom);
返回 cntEyeBottom;
}
简而言之,此函数根据左右眼角的两个点将眼睛的底部恢复为半椭圆。实际上,用于近似眼睛区域,该函数仅基于两个点定义椭圆参数:cv::ellipse2Poly()

椭圆中心和X半轴由两个眼点计算;X
Y 半轴根据平均眼宽为其长度的 1/3 的假设计算;Y1/3
起点和终点角为 0 和 180(请参阅文档);cv::ellipse()
角度增量:在轮廓中产生多少点;
轴的倾斜角度。
使用 而不是 just in 函数是必不可少的,因为它允许根据 和 符号返回负值,因此即使在颠倒的面排列的情况下,我们也可以获得正确的角度(当然,如果我们按正确的顺序排列点)。atan2()atan()custom::getLineInclinationAngleDegrees()xy

获得额头轮廓
该功能近似于额头轮廓:

内嵌轮廓自定义::getForeheadEllipse(const cv:😛 oint &ptJawLeft,
const cv:😛 oint &ptJawRight,
const cv:😛 oint &ptJawLower)
{
轮廓 cnt额头;
下巴顶部两个点之间的点:
const cv:😛 oint ptFaceCenter((ptJawLeft + ptJawRight) / 2);
这将是椭圆的中心。
下颌与垂直线之间的角度:
const int angFace = getLineInclinationAngleDegrees(ptJawLeft, ptJawRight);
这将是椭圆的倾角
计算椭圆的半轴:
const double jawWidth = cv::norm(ptJawLeft - ptJawRight);
额头宽度等于下颌宽度,我们需要一个半轴:
常量 int axisX = toIntRounded(jawWidth / 2.0);
const double jawHeight = cv::norm(ptFaceCenter - ptJawLower);
根据研究,平均额头约为 2/3
下巴:
常量 int axisY = toIntRounded(jawHeight * 2 / 3.0);
我们需要椭圆的上部:
静态 constexpr int kAngForeheadStart = 180;
静态 constexpr int kAngForeheadEnd = 360;
cv::ellipse2Poly(ptFaceCenter, cv::Size(axisX, axisY), angFace, kAngForeheadStart, kAngForeheadEnd,
config::kAngDelta, cntForehead);
返回 cntForehead;
}
由于我们在检测到的地标中只有下颌点,因此我们必须根据下颌的三个点获得一个半椭圆:最左边、最右边和最低的一点。假设下颌宽度等于前额宽度,后者使用左右点计算。说到Y轴,我们没有点可以直接得到,而是假设额头高度大约是下颌高度的2/3,可以从脸部中心(左右点之间的中间)和下颌最低点算出来。Y2/3
绘图蒙版
当我们拥有所需的所有轮廓时,我们就可以绘制面具:

    cv::GMat mskSharp = custom::GFillPolyGContours::on(gimgIn, garElsConts);// |
    cv::GMat mskSharpG = cv::gapi::gaussianBlur(mskSharp, config::kGKernelSize, // |

config::kGSigma);// |
cv::GMat mskBlur = custom::GFillPolyGContours::on(gimgIn, garFaceConts);// |
cv::GMat mskBlurG = cv::gapi::gaussianBlur(mskBlur, config::kGKernelSize, // |
config::kGSigma);// |绘制蒙版
mask() 中的第一个参数是 Blur,因为我们想从 // |
BlurG 下一步: // |
cv::GMat mskBlurFinal = mskBlurG - cv::gapi::mask(mskBlurG, mskSharpG);// |
cv::GMat mskFacesGaussed = mskBlurFinal + mskSharpG;// |
cv::GMat mskFacesWhite = cv::gapi::threshold(mskFacesGaussed, 0, 255, cv::THRESH_BINARY);// |
cv::GMat mskNoFaces = cv::gapi::bitwise_not(mskFacesWhite);// |
获得口罩的步骤是:

“夏普”掩码计算:
填充应锐化的轮廓;
模糊它以获得“锐利”蒙版(mskSharpG);
“双边”掩码计算:
充分填充所有面部轮廓;
模糊它;
减去与“尖锐”掩模相交的区域,得到“双边”掩码(mskBlurFinal);
背景掩码计算:
添加两个先前的蒙版
将结果的所有非零像素设置为 255(由cv::gapi::threshold())
还原输出 (by ) 以获取背景蒙版 ()。cv::gapi::bitwise_notmskNoFaces
配置和运行管道
一旦图形完全表达出来,我们最终就可以编译它并在真实数据上运行。G-API 图编译是 G-API 框架真正了解要使用的内核和网络的阶段。此配置通过 G-API 编译参数进行。

DNN 参数
此示例使用 OpenVINO™ 工具套件推理引擎后端进行 DL 推理,其配置方式如下:

auto faceParams = cv::gapi::ie::P arams<custom::FaceDetector>
{
    /*std::string*/ faceXmlPath,
    /*std::string*/ faceBinPath,
    /*std::string*/ faceDevice
};
auto landmParams = cv::gapi::ie::P arams<custom::LandmDetector>
{
    /*std::string*/ landmXmlPath,
    /*std::string*/ landmBinPath,
    /*std::string*/ landmDevice
};

每个对象都与其模板参数中指定的网络相关。我们应该将在本教程早期定义的网络类型传递到那里。cv::gapi::ie::Params<>G_API_NET()

然后,网络参数被包装在:cv::gapi::NetworkPackage

自动网络 = cv::gapi::networks(faceParams, landmParams);

有关“人脸分析管道”(配置管道部分)中的更多详细信息。

内核软件包
在此示例中,我们使用了大量自定义内核,此外,我们还使用 Fluid 后端来优化 G-API 标准内核的内存(如果适用)。生成的内核包如下所示:

auto customKernels = cv::gapi::kernels<custom::GCPUBilateralFilter,

custom::GCPULaplacian,
custom::GCPUFillPolyGContours,
自定义::GCPUPolyLines,
custom::GCPURectangle,
自定义::GCPUFacePostProc,
自定义::GCPULandmPostProc,
自定义::GCPUGetContours>();
自动内核 = cv::gapi::combine(cv::gapi::core::fluid::kernels(),
customKernels);
编译流式处理管道
G-API 优化了在“流”模式下编译时视频流的执行。

    cv::GStreamingCompiled stream = pipeline.compileStreaming(cv::compile_args(内核、网络));

有关此内容的详细信息,请参阅“人脸分析管道”(配置管道部分)。

运行流式处理管道
为了运行 G-API 流式处理管道,我们只需要指定输入视频源,调用 ,然后获取管道处理结果:cv::GStreamingCompiled::start()

    if (parser.has(“输入”))
    {

流。setSource(cv::gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(parser.get<cv::String>(“输入”)));
}
auto out_vector = cv::gout(imgBeautif, imgShow, vctFaceConts,
vctElsConts、vctRects);
流。开始();
avg.start();
而 (流。运行())
{
if (!stream.try_pull(std::move(out_vector)))
{
使用 try_pull() 获取数据。
如果没有数据,请让 UI 刷新(并处理按键)
if (cv::waitKey(1) >= 0) 中断;
否则继续;
}
帧++;
如有必要,绘制面框和地标:
if (flgLandmarks == true)
{
cv::p olylines(imgShow, vctFaceConts, config::kClosedLine,
config::kClrYellow);
cv::p olylines(imgShow, vctElsConts, config::kClosedLine,
config::kClrYellow);
}
if (flgBoxes == 真)
for (自动整形:vctRects)
cv::rectangle(imgShow, rect, config::kClrGreen);
cv::imshow(config::kWinInput, imgShow);
cv::imshow(config::kWinFaceBeautification, imgBeautif);
}
一旦结果准备就绪并可以从管道中提取,我们就会将其显示在屏幕上并处理 GUI 事件。

有关详细信息,请参阅“人脸分析管道”教程中的“运行管道”部分。

结论
本教程有两个目标:展示 OpenCV 4.2 中引入的 G-API 全新功能的使用,并给出对示例人脸美化算法的基本了解。

算法应用结果:
在这里插入图片描述

面部美化示例
在测试机器(Intel® Core™ i7-8700)上,G-API优化的视频流水线比其串行(非流水线)版本高出2.7倍,这意味着对于这样一个不平凡的图形,适当的流水线可以带来近3倍的性能提升。

在线教程

  • 麻省理工学院人工智能视频教程 – 麻省理工人工智能课程
  • 人工智能入门 – 人工智能基础学习。Peter Norvig举办的课程
  • EdX 人工智能 – 此课程讲授人工智能计算机系统设计的基本概念和技术。
  • 人工智能中的计划 – 计划是人工智能系统的基础部分之一。在这个课程中,你将会学习到让机器人执行一系列动作所需要的基本算法。
  • 机器人人工智能 – 这个课程将会教授你实现人工智能的基本方法,包括:概率推算,计划和搜索,本地化,跟踪和控制,全部都是围绕有关机器人设计。
  • 机器学习 – 有指导和无指导情况下的基本机器学习算法
  • 机器学习中的神经网络 – 智能神经网络上的算法和实践经验
  • 斯坦福统计学习
    有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓

请添加图片描述

人工智能书籍

  • OpenCV(中文版).(布拉德斯基等)
  • OpenCV+3计算机视觉++Python语言实现+第二版
  • OpenCV3编程入门 毛星云编著
  • 数字图像处理_第三版
  • 人工智能:一种现代的方法
  • 深度学习面试宝典
  • 深度学习之PyTorch物体检测实战
  • 吴恩达DeepLearning.ai中文版笔记
  • 计算机视觉中的多视图几何
  • PyTorch-官方推荐教程-英文版
  • 《神经网络与深度学习》(邱锡鹏-20191121)

  • 在这里插入图片描述

第一阶段:零基础入门(3-6个月)

新手应首先通过少而精的学习,看到全景图,建立大局观。 通过完成小实验,建立信心,才能避免“从入门到放弃”的尴尬。因此,第一阶段只推荐4本最必要的书(而且这些书到了第二、三阶段也能继续用),入门以后,在后续学习中再“哪里不会补哪里”即可。

第二阶段:基础进阶(3-6个月)

熟读《机器学习算法的数学解析与Python实现》并动手实践后,你已经对机器学习有了基本的了解,不再是小白了。这时可以开始触类旁通,学习热门技术,加强实践水平。在深入学习的同时,也可以探索自己感兴趣的方向,为求职面试打好基础。

第三阶段:工作应用

这一阶段你已经不再需要引导,只需要一些推荐书目。如果你从入门时就确认了未来的工作方向,可以在第二阶段就提前阅读相关入门书籍(对应“商业落地五大方向”中的前两本),然后再“哪里不会补哪里”。

有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1451556.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows下体验Stable Diffusion 近距离感受AI魔法绘画

🌹作者主页:青花锁 🌹简介:Java领域优质创作者🏆、Java微服务架构公号作者😄 🌹简历模板、学习资料、面试题库、技术互助 🌹文末获取联系方式 📝 往期专栏回顾 专栏描述Java项目实战介绍Java组件安装、使用;手写框架等Aws服务器实战Aws Linux服务器上操作ngin…

25.原型链和原型(非常重要),听说你还没搞懂??

1. 对原型、原型链的理解 在JavaScript中是使用构造函数来新建一个对象的&#xff0c;每一个构造函数的内部都有一个 prototype 属性&#xff0c;它的属性值是一个对象&#xff0c;这个对象包含了可以由该构造函数的所有实例共享的属性和方法。当使用构造函数新建一个对象后&a…

2024-02-16 AIGC-数字人-平台调研-记录

摘要: 2024-02-16 AIGC-数字人-平台调研 需求分析: 数字人-平台调研 南京硅基智能北京风平智能[风平科技]品达集团[杭州品达企服科技(集团)有限公司]花脸数字技术灰豚数字人[温州专帮信息科技有限公司]魔珐科技数字栩生公司官网guiji-ows风平智能 - 领先的AIGC解决方案提供商。…

小白水平理解面试经典题目LeetCode 102 Binary Tree Level Order Traversal【二叉树】

102. 二叉树层次顺序遍历 小白渣翻译 给定二叉树的 root &#xff0c;返回其节点值的层序遍历。 &#xff08;即从左到右&#xff0c;逐级&#xff09;。 例子 小白教室做题 在大学某个自习的下午&#xff0c;小白坐在教室看到这道题。想想自己曾经和白月光做题&#xff0c…

工业数据采集的时间不确定性及PLC-Recorder的通道偏移功能

目录 一、缘起 二、效果展示 三、设置方法 四、小结 一、缘起 大家都知道采集软件首先要尽可能还原数据原来的状态&#xff0c;给用户提供一个可以信赖的参考。但是&#xff0c;数据采集又有很多随机因素&#xff1a;Windows是一个周期不严格的系统、以太网通讯有时间波动、…

【华为数通HCIP | 网络工程师】H12-831刷题日记 题目+解析(2)

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

wps使用方法(包括:插入倒三角符号,字母上面加横线,将word中的所有英文设置为time new roman)

倒三角符号 字母上面加横线 将word中的所有英文设置为time new roman ctrla选中全文

p图考试,搜题软件哪个好?如何利用大学搜题工具查找特定学科的题目? #微信#知识分享

大学生必备&#xff0c;这条笔记大数据一定定要推给刚上大学的学弟学妹&#xff01;&#xff01; 1.三羊搜题 这是个微信公众号 是个公众号&#xff0c;支持文字、语音、截图搜题&#xff0c;截图搜题&#xff0c;同时也支持相似题查看及解析&#xff0c;因为可以在电脑上使…

一款基于MSSQL数据库注入的利用工具-SqlmapXPlus

一、基本介绍 在众多的地区性攻防演练中&#xff0c;SQL Server数据库堆叠注入仍有较高的爆洞频率&#xff0c;但因为一些常见的演练场景限制&#xff0c;如不出网、低权限、站库分离、终端防护、上线困难、权限维持繁琐等&#xff0c;仅一个–os-shell已经难满足我们的需求。…

apk反编译修改教程系列---简单去除apk登陆 修改vip与一些反编译基础常识【十二】

往期教程&#xff1a; 安卓玩机-----反编译apk 修改apk 去广告 去弹窗等操作中的一些常识apk反编译修改教程系列-----修改apk应用名称 任意修改名称 签名【一】 apk反编译修改教程系列-----任意修改apk版本号 版本名 防止自动更新【二】 apk反编译修改教程系列-----修改apk中…

智能汽车专题:华为赋能下的车企,具备下一阶段Winner的潜质

今天分享的是智能汽车系列深度研究报告&#xff1a;《智能汽车专题&#xff1a;华为赋能下的车企&#xff0c;具备下一阶段Winner的潜质》。 &#xff08;报告出品方&#xff1a;广发证券&#xff09; 报告共计&#xff1a;27页 华为汽车业务历史沿革 基于ICT领域的深厚积累…

Medical Boundary Diffusion Modelfor Skin Lesion Segmentation

皮肤病灶分割的医学边界扩散模型 摘要 由于多尺度边界关注和特征增强模块的进步&#xff0c;皮肤镜图像中的皮肤病变分割最近取得了成功。然而&#xff0c;现有的方法依赖于端到端学习范式&#xff0c;直接输入图像和输出分割图&#xff0c;经常与极其困难的边界作斗争&#…

小程序或者浏览器chrome访问的时候出现307 interval redicrect内部http自动跳转到https产生的原理分析及解决方案

#小李子9479# 出现的情况如下&#xff0c;即我们访问http的时候&#xff0c;它会自动307重定向到https,产生的原因是&#xff0c; 当你通过https访问过一个没有配置证书的http的网站之后&#xff0c;你再访问http的时候&#xff0c;它就会自动跳转到https&#xff0c;导致访问…

Harris关键点检测原理简介

一、2D Harris 二、 3D Harris Harris关键点检测以及SAC-IA粗配准-CSDN博客

Dockerfile 常用指令

1、FROM 指定base镜像。 2、Docker history 显示镜像的构建历史&#xff0c;也就是Dockerfile的执行过程。 Missing 表示无法获取IMAGE ID&#xff0c;通常从Docker Hub下载的镜像会有这个问题。 3、调试Dockerfile&#xff0c;使用sudo docker run -it XXXX&#xff0c;XXXX…

基于SSM的社区疫情防控管理系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的社区疫情防控管理系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spri…

AI Infra论文阅读之LIGHTSEQ(LLM长文本训练的Infra工作)

感觉这篇paper有几个亮点&#xff0c;首先把Megatron-LM的Self-Attention模块的模型并行方式变成序列并行&#xff0c;优化了通信量&#xff0c;同时通过计算和通信重叠近一步压缩了训练迭代时间。另外&#xff0c;在使用重计算的时候发现当前Huggingface/Megatron-LM的重计算策…

骨传导耳机是什么?如何选择骨传导耳机不会踩雷?

骨传导耳机是利用骨传导技术研发而成一种新型蓝牙耳机&#xff0c;其传声方式很独特&#xff0c;不通过空气传导&#xff0c;而是通过人体骨骼来传递声音。 详细传声原理请看下图&#xff1a; 随着骨传导耳机逐渐热门&#xff0c;如何选购耳机成为了问题&#xff0c;下面跟大家…

【十九】【C++】 priority_queue简单使用和仿函数

priority_queue文档介绍翻译 优先队列是一种容器适配器&#xff0c;专门设计成其中的第一个元素始终是根据某种严格的弱排序准则最大的元素。 这种上下文类似于堆&#xff0c;其中元素可以在任何时刻插入&#xff0c;而只能检索最大堆元素&#xff08;在优先队列中顶部的元素&a…

ES入门知识点总结

目录 倒排索引 倒排索引 Elasticsearch的倒排索引是一种数据结构&#xff0c;用于加快基于文本的搜索操作。它的主要优势在于能够快速找到包含特定单词的文档。 倒排索引的构建过程如下&#xff1a; 文档分词&#xff1a;将文档内容分割成单独的词&#xff08;或者更小的词元…