当前位置: 首页 > news >正文

OpenCV实战(11)——形态学变换详解

OpenCV实战(11)——形态学变换详解

    • 0. 前言
    • 1. 腐蚀和膨胀运算
      • 1.1 腐蚀和膨胀基础
      • 1.2 使用形态学滤波器执行图像腐蚀和膨胀运算
    • 2. 开运算和闭运算
      • 2.1 使用形态学滤波器执行图像开运算和闭运算
    • 3. 形态学变换应用
      • 3.1 使用形态学滤波器检测边缘
      • 3.2 使用形态学滤波器检测边缘和角点
    • 4. 完整代码
    • 小结
    • 系列链接

0. 前言

形态学变换( Morphological transformations )通常是在二值图像上执行、基于图像形状的操作。其具体的操作由核结构元素决定,它决定了操作的性质。膨胀和腐蚀是形态学变换领域的两个基本算子,此外,开运算和闭运算是两个重要的运算,它们可以通过上述两个运算(膨胀和腐蚀)获得。

1. 腐蚀和膨胀运算

1.1 腐蚀和膨胀基础

腐蚀 (Erosion) 和膨胀 (Dilation) 是最基本的形态学算子,因此,我们将首先介绍这两个基本算子。数学形态学的基本组成部分是结构元素,结构元素可以简单地理解为定义原点(也称为锚点)的像素配置,如下图中的正方形。应用形态学滤波器需要使用此结构元素检测图像的每个像素,当结构元素的原点与给定像素对齐时,它与图像的交集定义了一组像素,在这些像素(下图中的九个阴影像素)上应用了特定的形态学操作。原则上,结构元素可以是任何形状,但出于效率考虑,通常使用简单的形状,例如正方形、圆形或以原点为中心的菱形。

结构元素

1.2 使用形态学滤波器执行图像腐蚀和膨胀运算

由于形态学滤波器通常作用于二值图像,因此我们使用通过阈值化创建的二值图像。然而,由于在形态学中通常用白色像素值表示前景对象,用黑色像素值表示背景对象,因此我们需要对原二值图像求补(即用黑色像素值表示前景对象,用白色像素值表示背景对象)。在形态学中,下图可以认为是由阈值化创建的二值图像的补:

二值图像
与其他形态学滤波器一样,腐蚀和膨胀两个滤波器对由结构元素定义的每个像素周围的像素集(或邻域)进行操作,当应用于给定像素时,结构元素的锚点与该像素位置对齐,并且与结构元素相交的所有像素都包含在当前集合中。腐蚀使用在定义的像素集中找到的最小像素值替换当前像素。膨胀是腐蚀的互补算子,它使用定义的像素集中找到的最大像素值替换当前像素。由于输入二值图像仅包含黑色 (0) 和白色 (255) 像素,因此每个像素都被替换为白色或黑色像素。腐蚀和膨胀在 OpenCV 中分别使用 cv::erodecv::dilate 函数实现。

(1) 首先,读取二值图片:

// 读取二值图像
cv::Mat binary;
binary = cv::imread("binary.png", 0);

(2) 应用 cv::erode 函数:

// 腐蚀图像
cv::Mat eroded;
cv::erode(image, eroded, cv::Mat());

应用腐蚀形态学滤波器后,可以得到以下结果:

腐蚀
(3) 使用 cv::dilate 膨胀图像:

// 膨胀图像
cv::Mat dilated;
cv::dilate(image, dilated, cv::Mat());

应用膨胀形态学滤波器后,可以得到以下结果:

膨胀
接下来,我们从这两个运算符的执行效果进行理解,对于腐蚀算子,如果结构元素置于给定像素位置与背景相接触(即,相交集中的像素之一是黑色),则该像素将被赋值为黑色并归属至背景;在膨胀算子中,如果背景像素上的结构元素接触前景对象,则该像素将被赋值为白色值。
这就解释了为什么在腐蚀图像中物体的尺寸会减小(形状被腐蚀),而一些小物体由于可能被认为是嘈杂的背景像素会被完全消除;而膨胀运算后的物体将会变大,并且物体内部的一些孔洞会被填满。默认情况下,OpenCV 使用 3 x 3 方形结构元素,当调用函数时第三个参数指定为空矩阵(即 cv::Mat() )时,将使用此默认结构元素,我们可以通过使用非零元素定义结构元素矩阵,指定所需大小(和形状)的结构元素。例如,我们可以使用以下代码定义 7 x 7 结构元素:

cv::Mat element(7, 7, CV_8U, cv::Scalar(1));
cv::erode(image, eroded, element);

在这种情况下,效果更加明显,如下图所示:

腐蚀操作

我们可以简单的在图像上重复应用相同的结构元素,cv::erodecv::dilate 这两个函数都有一个可选参数来指定重复次数:

// 腐蚀同一图像三次
cv::erode(image, eroded, cv::Mat(), cv::Point(-1, -1), 3);

原点参数 cv::Point(-1,-1) 表示原点位于矩阵的中心(默认);原点也可以定义在结构元素的其他位置。使用以上代码得到的图像与我们使用 7 x 7 结构元素获得的图像相同。实际上,腐蚀图像两次类似于腐蚀结构元素尺寸扩大的图像,这一规律也适用于膨胀算子。
由于背景/前景的概念是相对的,用结构元素腐蚀前景对象可也以看作是图像背景部分的膨胀,这是腐蚀/膨胀算子的基本属性。换句话说:

  • 图像的腐蚀等价于互补图像膨胀的补
  • 图像的膨胀等价于互补图像腐蚀的补
    虽然我们在本节主要介绍了如何将形态学滤波器应用于二值图像,但这些滤波器同样也可以应用于灰度或彩色图像。
    OpenCV 形态学函数支持就地处理,即可以使用输入图像作为目标图像:
cv::erode(image,image,cv::Mat());

OpenCV 在函数内部创建所需的临时图像,以使其正常工作。

2. 开运算和闭运算

2.1 使用形态学滤波器执行图像开运算和闭运算

上一节中,我们介绍了两个基本的形态学算子——膨胀和腐蚀。利用这些基本算子,我们可以定义其他运算符,本节将介绍开运算和闭运算。为了应用高级形态学滤波器,需要使用 cv::morphologyEx 函数。

(1) 要创建闭运算或开运算符,必须创建一个 cv::Mat 元素用作开/闭运算核:

cv::Mat element5(5, 5, CV_8U, cv::Scalar(1));

(2) 创建另一个 cv::Mat 存储应用形态学运算符后的结果:

cv::Mat closed;
cv::Mat opened;

(3) 最后,应用闭或开运算符。为了创建闭运算符,我们使用 cv::MORPH_CLOSE 参数调用 cv::morphologyEx 函数:

cv::morphologyEx(image, closed, // 输入和输出图像cv::MORPH_CLOSE,        // 操作算子element5);              // 结构元素

如果我们使用二值图像作为输入,结果如下图所示:

闭运算

(4) 使用 cv::MORPH_OPEN 作为参数调用 cv::morphologyEx 函数,可以创建开运算:

cv::morphologyEx(image, opened, cv::MORPH_OPEN, element5);

应用形态学开运算可以得到以下图像:

开运算

(5) 开和闭滤波器是根据基本腐蚀和膨胀操作定义的,闭运算被定义为膨胀图像后腐蚀,开运算被定义为腐蚀图像后膨胀。因此,可以使用以下代码计算图像的闭:

// 1. 膨胀原始图像
cv::Mat result;
cv::dilate(image, result, element5);
// 2. 腐蚀膨胀后的图像
cv::erode(result, result, element5);

可以通过交换这两个函数的调用顺序得到开滤波器。在使用闭滤波器的结果图像中,可以看到白色前景对象中的小孔被填充,滤波器还会将几个相邻的对象连接在一起。实际上,任何无法完全包含结构元素的过小的孔或间隙都将被滤波器消除。相反的,开滤波器会消除图像中的小物体,所有无法包含结构元素的过小对象都会被消除。
开/闭滤波器通常用于对象检测,闭滤波器可以连接被错误分割成小块的对象,而开滤波器可以去除由图像噪声引入的小斑点。因此,根据具体应用可以按不同顺序使用它们。如果二值图像连续应用闭和开运算,将得到一个仅显示场景中主要对象的图像,如下图所示。如果我们希望优先过滤噪声,也可以在闭滤波器之前应用开滤波器,但这会消除一些碎片化对象:

在闭滤波器之前应用开滤波器
在图像上多次应用相同的开(或闭)运算符不会产生任何效果。实际上,由于孔已被第一个开滤波器填充,因此附加应用相同的滤波器不会对图像产生其他变化。在数学上,这些运算符被称为幂等运算符。

3. 形态学变换应用

形态学滤波器也可用于检测图像中的特定特征。在本节中,我们将学习如何检测灰度图像中的轮廓和角点。

3.1 使用形态学滤波器检测边缘

(1) 通过使用适当滤波器参数调用 cv::morphologyEx 函数提取要检测的图像的边缘:

// 使用 3x3 结构元素获取梯度图像
cv::Mat result;
cv::morphologyEx(image, result, cv::MORPH_GRADIENT, cv::Mat());
// 使用阈值获取二值图像
int threshold(80);
cv::threshold(result, result, threshold, 255, cv::THRESH_BINARY);

可以得到以下结果图像:

检测图像边缘

理解形态学算子对灰度图像的影响时,可以将图像视为拓扑浮雕,其中灰度与高程(或海拔)相对应。从这个角度而言,明亮的区域对应山脉,而黑暗的区域对应山谷。此外,由于边缘对应于暗像素和亮像素之间的快速过渡,因此可以将其理解为陡峭的悬崖。如果在这样的地形上应用腐蚀算子,最终将用邻域中的最低值替换每个像素,从而降低其高度。因此,随着山谷的扩大,悬崖将被腐蚀。膨胀具有完全相反的效果;也就是说,悬崖将膨胀而山谷相应的会缩小。但在这两种情况下,高原(即强度恒定的区域)均将保持相对不变。
基于此,我们可以得到一种检测图像边缘(悬崖)的简单方法,即计算膨胀和腐蚀图像之间的差。由于这两种变换后的得到的图像主要在与边缘位置不同,因此相减将得到图像边缘。我们可以使用 cv::MORPH_GRADIENT 参数调用 cv::morphologyEx 函数完成以上操作。显然,结构元素越大,检测到的边缘就越宽,这种边缘检测算子也称为 Beucher 梯度(之后的学习中将更详细地讨论图像梯度的概念)。通过简单地从膨胀图像中减去原始图像或从原始图像中减去图像图像,也可以获得类似的结果,但产生的边缘会更细。

3.2 使用形态学滤波器检测边缘和角点

(1) 为了使用形态学检测角点,我们可以定义 MorphoFeatures 类:

class MorphoFeatures {private:// 用于产生二值图像的阈值int threshold;// 用于角点检测的结构元素cv::Mat_<uchar> cross;cv::Mat_<uchar> diamond;cv::Mat_<uchar> square;cv::Mat_<uchar> x;

(2) 使用形态学角点检测角点需要连续应用几个不同的形态学滤波器,为此我们需要使用非方形结构元素,实际上,在构造函数中定义了四个不同的结构元素,形状分别为正方形、菱形、十字形和 X 形,为简单起见,这些结构元素尺寸均为 5 x 5

public:MorphoFeatures() : threshold(-1), cross(5, 5), diamond(5, 5),square(5, 5), x(5, 5) {// 创建十字形结构元素cross <<    0, 0, 1, 0, 0,0, 0, 1, 0, 0,1, 1, 1, 1, 1,0, 0, 1, 0, 0,0, 0, 1, 0, 0;// 菱形结构元素diamond <<  0, 0, 1, 0, 0,0, 1, 1, 1, 0,1, 1, 1, 1, 1,0, 1, 1, 1, 0,0, 0, 1, 0, 0;// 方形结构元素square <<   1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1;// x形结构元素x <<        1, 0, 0, 0, 1,0, 1, 0, 1, 0,0, 0, 1, 0, 0,0, 1, 0, 1, 0,1, 0, 0, 0, 1;}

(4) 在角点特征检测中,顺序应用这些结构元素以获得结果角点图:

// 角点检测
cv::Mat getCorners(const cv::Mat& image) {cv::Mat result;// 膨胀cv::dilate(image, result, cross);// 腐蚀cv::erode(result, result, diamond);cv::Mat result2;// 膨胀cv::dilate(image, result2, x);// 腐蚀cv::erode(result2, result2, square);// 角点cv::absdiff(result2, result, result);applyThreshold(result);return result;
}

(5) 在图像上检测角点:

MorphoFeatures morpho;
morpho.setThreshold(35);
// 角点检测
cv::Mat corners;
corners = morpho.getCorners(gray);
// 在图像中显示角点
morpho.drawOnImage(corners, gray);
cv::namedWindow("Corners on Image");
cv::imshow("Corners on Image", gray);

在图像中,检测到的角点显示为圆圈,如下图所示:

角点

角点检测相较更为复杂,因为它需要使用四种不同的结构元素。在 OpenCV 中并没有直接实现角点检测算子,但我们可以通过定义和组合不同形状的结构元素实现。通过用两种不同的结构元素来膨胀和腐蚀图像实现图像闭运算。使用这些元素令图像中直线保持不变,但不同结构元素也会影响角点处的边。以单个白色方块组成的简单图像为例介绍这种非对称闭操作的效果:

非对称操作

上图中,第一个方块是原始图像。当用十字形结构元素执行膨胀操作时,方块边缘会被扩展,除了十字形不碰到方形的角点,如上图中间的方块所示。然后,膨胀后的图像被菱形结构元素腐蚀,腐蚀操作会令大多数边缘恢复到原来的位置,但由于方形的角没有被膨胀,所以其会被进一步腐蚀,如上图中最右边的正方形所示,可以看到其角已经消失。使用 X 形和方形结构元素重复以上过程,由于 X 形是十字形结构元素的旋转版本,因此可以捕获 45 度方向的角。最后,对两个结果进行差分便可以提取角点特征。

4. 完整代码

头文件 (morphoFeatures.h) 完整代码如下:

#if !defined MFEATURES
#define MFEATURES#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>class MorphoFeatures {private:// 用于产生二值图像的阈值int threshold;// 用于角点检测的结构元素cv::Mat_<uchar> cross;cv::Mat_<uchar> diamond;cv::Mat_<uchar> square;cv::Mat_<uchar> x;public:MorphoFeatures() : threshold(-1), cross(5, 5), diamond(5, 5),square(5, 5), x(5, 5) {// 创建十字形结构元素cross <<    0, 0, 1, 0, 0,0, 0, 1, 0, 0,1, 1, 1, 1, 1,0, 0, 1, 0, 0,0, 0, 1, 0, 0;// 菱形结构元素diamond <<  0, 0, 1, 0, 0,0, 1, 1, 1, 0,1, 1, 1, 1, 1,0, 1, 1, 1, 0,0, 0, 1, 0, 0;// 方形结构元素square <<   1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1;// x形结构元素x <<        1, 0, 0, 0, 1,0, 1, 0, 1, 0,0, 0, 1, 0, 0,0, 1, 0, 1, 0,1, 0, 0, 0, 1;}void setThreshold(int t) {threshold = t;}int getThreshold() {return threshold;}void applyThreshold(cv::Mat& result) {if (threshold > 0) {cv::threshold(result, result, threshold, 255, cv::THRESH_BINARY_INV);}}// 直线检测cv::Mat getEdges(const cv::Mat& image) {cv::Mat result;cv::morphologyEx(image, result, cv::MORPH_GRADIENT, cv::Mat());applyThreshold(result);return result;}// 角点检测cv::Mat getCorners(const cv::Mat& image) {cv::Mat result;// 膨胀cv::dilate(image, result, cross);// 腐蚀cv::erode(result, result, diamond);cv::Mat result2;// 膨胀cv::dilate(image, result2, x);// 腐蚀cv::erode(result2, result2, square);// 角点cv::absdiff(result2, result, result);applyThreshold(result);return result;}void drawOnImage(const cv::Mat& binary, cv::Mat& image) {cv::Mat_<uchar>::const_iterator it = binary.begin<uchar>();cv::Mat_<uchar>::const_iterator itend = binary.end<uchar>();for (int i=0; it!=itend; ++it, ++i) {if (!*it) {cv::circle(image, cv::Point(i%image.step, i/image.step),5,cv::Scalar(255, 0, 0));}}}
};#endif

主文件 (morphology.cpp) 完整代码如下所示:

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>int main() {// 读取图像cv::Mat image = cv::imread("binary.png");if (!image.data) return 0;cv::namedWindow("Image");cv::imshow("Image", image);// 腐蚀图像cv::Mat eroded;cv::erode(image, eroded, cv::Mat());cv::namedWindow("Eroded Image");cv::imshow("Eroded Image", eroded);// 膨胀图像cv::Mat dilated;cv::dilate(image, dilated, cv::Mat());cv::namedWindow("Dilated Image");cv::imshow("Dilated Image",dilated);// 使用一个较大的结构元素腐蚀图像cv::Mat element(7, 7, CV_8U, cv::Scalar(1));cv::erode(image, eroded, element);cv::namedWindow("Eroded Image (7x7)");cv::imshow("Eroded Image (7x7)",eroded);// 腐蚀同一图像三次cv::erode(image, eroded, cv::Mat(), cv::Point(-1, -1), 3);cv::namedWindow("Eroded Image (3 times)");cv::imshow("Eroded Image (3 times)",eroded);// 图像闭运算cv::Mat element5(5, 5, CV_8U, cv::Scalar(1));cv::Mat closed;cv::morphologyEx(image, closed, // 输入和输出图像cv::MORPH_CLOSE,        // 操作算子element5);              // 结构元素cv::namedWindow("Closed Image");cv::imshow("Closed Image",closed);// 图像开运算cv::Mat opened;cv::morphologyEx(image, opened, cv::MORPH_OPEN, element5);cv::namedWindow("Opened Image");cv::imshow("Opened Image",opened);// 闭运算分解// 1. 膨胀原始图像cv::Mat result;cv::dilate(image, result, element5);// 2. 腐蚀膨胀后的图像cv::erode(result, result, element5);cv::namedWindow("Closed Image (2)");cv::imshow("Closed Image (2)", result);// 闭-开cv::morphologyEx(image, image, cv::MORPH_CLOSE, element5);cv::morphologyEx(image, image, cv::MORPH_OPEN, element5);cv::namedWindow("Closed|Opened Image");cv::imshow("Closed|Opened Image", image);cv::imwrite("binaryGroup.png", image);// 开-闭image = cv::imread("binary.png");cv::morphologyEx(image, image, cv::MORPH_OPEN, element5);cv::morphologyEx(image, image, cv::MORPH_CLOSE, element5);cv::namedWindow("Opened|Closed Image");cv::imshow("Opened|Closed Image",image);// 读取输入图像image = cv::imread("1.png", 0);if (!image.data) return 0;// 使用 3x3 结构元素获取梯度图像cv::morphologyEx(image, result, cv::MORPH_GRADIENT, cv::Mat());cv::namedWindow("Edge Image");cv::imshow("Edge Image", 255 - result);// 使用阈值获取二值图像int threshold(80);cv::threshold(result, result, threshold, 255, cv::THRESH_BINARY);cv::namedWindow("Thresholded Edge Image");cv::imshow("Thresholded Edge Image", result);// 使用 3x3 结构元素获取梯度图像cv::morphologyEx(image, result, cv::MORPH_GRADIENT, cv::Mat());// 读取输入图像image = cv::imread("10.png", 0);if (!image.data) return 0;cv::transpose(image, image);cv::flip(image, image, 0);// 使用 7x7 结构元素应用黑色顶帽变换 cv::Mat element7(7, 7, CV_8U, cv::Scalar(1));cv::morphologyEx(image, result, cv::MORPH_BLACKHAT, element7);cv::namedWindow("7x7 Black Top-hat Image");cv::imshow("7x7 Black Top-hat Image", 255-result);cv::waitKey();return 0;
}

主文件 (cornersDetection.cpp) 完整代码如下所示:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>#include "morphoFeatures.h"int main() {cv::Mat image = cv::imread("3.png");cv::Mat gray;cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);cv::namedWindow("Image");cv::imshow("Image", gray);MorphoFeatures morpho;morpho.setThreshold(35);// 角点检测cv::Mat corners;corners = morpho.getCorners(gray);// 在图像中显示角点morpho.drawOnImage(corners, gray);cv::namedWindow("Corners on Image");cv::imshow("Corners on Image", gray);cv::waitKey();return 0;
}

小结

形态学变换( Morphological transformations )通常是在二值图像上执行、基于图像形状的操作。本节,首先介绍了基本形态学算子,腐蚀与膨胀,并介绍了根据基本算子组合得到的开运算与闭运算,最后,利用形态学算子实现了经典的边缘/角点检测图像处理应用。

系列链接

OpenCV实战(1)——OpenCV与图像处理基础
OpenCV实战(2)——OpenCV核心数据结构
OpenCV实战(3)——图像感兴趣区域
OpenCV实战(4)——像素操作
OpenCV实战(5)——图像运算详解
OpenCV实战(6)——OpenCV策略设计模式
OpenCV实战(7)——OpenCV色彩空间转换
OpenCV实战(8)——直方图详解
OpenCV实战(9)——基于反向投影直方图检测图像内容
OpenCV实战(10)——积分图像详解

相关文章:

OpenCV实战(11)——形态学变换详解

OpenCV实战&#xff08;11&#xff09;——形态学变换详解0. 前言1. 腐蚀和膨胀运算1.1 腐蚀和膨胀基础1.2 使用形态学滤波器执行图像腐蚀和膨胀运算2. 开运算和闭运算2.1 使用形态学滤波器执行图像开运算和闭运算3. 形态学变换应用3.1 使用形态学滤波器检测边缘3.2 使用形态学…...

SPI协议详解(Standard SPI、Dual SPI和Queued SPI)

1、标准SPI 1.1、SPI接口的引脚 (1)SCLK&#xff1a;时钟线&#xff1b; (2)MOSI(master output slave input)&#xff1a;主设备输出&#xff0c;从设备输入&#xff0c;单向传输&#xff1b; (3)MISO(master input slave output)&#xff1a;主设备输入&#xff0c;从设备输…...

【代码随想录二刷】Day15-二叉树-C++

代码随想录二刷Day15 今日任务 层序遍历 226.翻转二叉树 101.对称二叉树 语言&#xff1a;C 层序遍历 102.二叉树的层序遍历 class Solution { public:vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> res;if(root NULL) …...

C++为什么能重夺年度语言?

目录一、爷青回1、年初依旧很多大新闻&#xff0c;其中一条就是TIOBE把年度编程语言颁给了C。2、这是什么概念&#xff1f;那一年Java的流行指数是14%。二、C为什么衰落三、C为什么重新流行1、C为什么重新流行起来了呢&#xff1f;2、C究竟做对了什么呢&#xff1f;3、根本原因…...

视频监控实时接入——以海康威视为例(2023.2.16)

海康威视实时视频监控接入学习 2023.2.16引言1、视频协议简介1.1 RTSP——Real Time Streaming Protocol&#xff08;实时流传输协议&#xff09;1.2 RTMP——Real Time Messaging Protocol&#xff08;实时消息传输协议&#xff09;1.3 HLS——HTTP Live Streaming&#xff08…...

推荐系统[一]:超详细知识介绍,一份完整的入门指南,解答推荐系统是什么。

1. 推荐算法的初步理解 如果说互联网的目标就是连接一切,那么推荐系统的作用就是建立更加有效率的连接,推荐系统可以更有效率的连接用户与内容和服务,节约了大量的时间和成本。 1.1 推荐系统主要解决问题 任务一:挖掘长尾:帮助用户找到想要的物品(音乐、商品、新闻),…...

新手小白入门必看!如何批量注册Twitter账号?

Twitter是目前海外比较流行的社媒营销平台&#xff0c;所以很多从事跨境电商行业的朋友都需要利用多个Twitter账号来推广营销&#xff0c;但是注册和管理多个Twitter账号其实并不是简单的事情。龙哥将会在这里详细讲讲该如何批量注册并且让这些账号不会因为关联被封号&#xff…...

虚拟环境的创建以及labelme的使用教程

本来打算是将这两部分分开的&#xff0c;但写完虚拟环境的创建似乎字数太少了&#xff0c;不过二者有关联&#xff0c;所以就放一起了。简单介绍一下&#xff0c;虚拟环境的创建有win11系统已经Ubuntu系统&#xff0c;labelme教程包括了下载及其使用的全部流程&#xff0c;以及…...

CSS中的BFC详细讲解(易懂)

带你用最简单的方式理解最全面的BFC~~~1.先了解最常见定位方案普通流元素按照其在 HTML 中的先后位置至上而下布局行内元素水平排列&#xff0c;直到当行被占满然后换行&#xff0c;块级元素则会被渲染为完整的一个新行所有元素默认都是普通流定位浮动元素首先按照普通流的位置…...

华为3面,官网显示面试通过了...开始泡池子,进入漫长等待期

背景&#xff1a; 现在双非本科&#xff0c;非计算机科班&#xff0c;有算法方面的奖&#xff0c;有嵌入式开发经历&#xff0c;官网显示面试通过&#xff0c;短信说录用情况在十个工作日内告知&#xff0c;看别人的说法应该是泡池子了。 全程视频面试&#xff0c;一天面完三…...

【新2023】华为OD机试 - 构成的正方形数量(Python)

构成的正方形数量 题目 输入 N 个互不相同的二维整数坐标, 求这 N 个坐标可以构成的正方形数量。(内积为零的两个向量垂直) 输入 第一行输入为 N,N 代表坐标数量,N为正整数。N <= 100 之后的 K 行输入为坐标 x y以空格分隔,x, y 为整数, -10 <= x, y <= 10 输…...

ElasticSearch之RestClient操作索引库和文档

前言&#xff1a;上文介绍了使用DSL语言操作索引库和文档&#xff0c;本篇文章将介绍使用Java中的RestClient来对索引库和文档进行操作。 希望能够加深自己的印象以及帮助到其他的小伙伴儿们&#x1f609;&#x1f609;。 如果文章有什么需要改进的地方还请大佬不吝赐教&#x…...

Lp正则化

一、L1 和 L2范数&#xff08;norm&#xff09;A norm is a mathematical thing that is applied to a vector. The norm of a vector maps vector values to values in [0,∞). In machine learning, norms are useful because they are used to express distances: this vect…...

云原生 -- Docker进阶(Docker-compose,Docker网络简单介绍)

Dockerfile的构建过程 每条保留字段必须为大写字母。Dockerfile每行只支持一条指令&#xff0c;但是每条指令可以带多个参数&#xff0c;并且每条保留字指令后面至少要带有一个参数。从上到下依次执行。每条指令都会创建一个新的镜像层&#xff0c;并提交新的镜像。 大致流程…...

taskset命令:让进程运行在指定CPU上

1. 操作场景 taskset命令&#xff0c;可用于进程的CPU调优&#xff0c;可以把云服务器上运行的某个进程&#xff0c;指定在某个CPU上工作。 本节操作指导用户使用taskset命令让进程运行在指定CPU上。 2. 操作步骤 2.1. 执行如下命令&#xff0c;查看云服务器CPU核数。 cat …...

Pod基本概念与Pod应用生命周期

Pod是一个逻辑抽象概念&#xff0c;kubernetes创建和管理的最小单元&#xff0c;一个Pod由一个容器或多个容器组成。特点&#xff1a;一个Pod可以理解为是一个应用实例&#xff0c;提供服务Pod中容器始终部署在一个Node上Pod中容器共享网络、存储资源Pod主要用法&#xff1a;运…...

DDL 数据定义语言

DDL 数据定义语言 目录概述一、库的管理1、库的创建2、库的修改【一般不修改&#xff0c;容易出现错误】3、库的删除二、表的管理【重要】1、表的创建2、表的修改3、表的删除4、表的复制 【可以跨库复制】练习题概述 数据定义语言 库和表的管理 一、库的管理 创建、修改、删除…...

设计模式概述

1. 概念 设计模式概念的提出&#xff1a;   设计模式最早于1977年在建筑设计行业中被 克里斯托夫亚历山大&#xff08;Christopher Alexander&#xff09; 在他的著作 《建筑模式语言&#xff1a;城镇、建筑、构造》 中提出。   软件工程界在1990年开始了设计模式话题的研…...

华为OD机试 - 箱子之形摆放(Python)| 真题+思路+考点+代码+岗位

箱子之形摆放 题目 有一批箱子(形式为字符串,设为str), 要求将这批箱子按从上到下以之字形的顺序摆放在宽度为 n 的空地,请输出箱子的摆放位置。 例如:箱子ABCDEFG,空地宽度为3,摆放结果如图: 则输出结果为: AFG BE CD 输入 输入一行字符串,通过空格分隔,前面部…...

第九章:创建用户和用户权限

Windows&#xff1a;创建用户&#xff1a;第一种方法创建用户&#xff1a;先点右上角的工具&#xff0c;然后点击AD用户和计算机双击skills.com打开目录&#xff0c;再双击Users&#xff0c;进入文件夹中在右框中右击空白处&#xff0c;新建用户填充好用户信息后点击下一步然后…...

如何制定人生目标

一、如何分解目标 人生终极目标并不一定要多详细精确&#xff0c;但一定要被分解&#xff0c;要分成长期目标、中期目标和一系列的短期目标&#xff0c;其中短期目标又可以分解为你能够马上操作的一个个的小目标。 二、目标制定的原则 目标制定遵循 SMART-W 原则&#xff1a; …...

用户认证概述

文章目录一、用户身份认证1.1 单一服务器模式1.2 SSO&#xff08;Single Sign On&#xff09;模式1.3 Token模式二、JWT令牌2.1 JWT 令牌说明2.2 JWT令牌的组成2.3 JWT 问题和趋势2.4 JWT 测试一、用户身份认证 1.1 单一服务器模式 一般过程如下&#xff1a; 用户向服务器发送…...

XQuery FLWOR + HTML

XML 实例文档 我们将在下面的例子中继续使用这个 "books.xml" 文档&#xff08;与上一节中的文件相同&#xff09;。 在您的浏览器中查看 "books.xml" 文件。 在一个 HTML 列表中提交结果 请看下面的 XQuery FLWOR 表达式&#xff1a; for $x in doc(&…...

MySQL用户管理

文章目录MySQL用户管理用户用户信息创建用户修改用户密码删除用户数据库的权限MySQL中的权限给用户授权回收权限MySQL用户管理 与Linux操作系统类似&#xff0c;MySQL中也有超级用户和普通用户之分。如果一个用户只需要访问MySQL中的某一个数据库&#xff0c;甚至数据库中的某…...

C++【模板初阶】

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; C修行之路 &#x1f38a;每篇一句&#xff1a; 图片来源 No one saves us but ourselves, no one can and no one may. We ourselves must walk the path. 除了我们自己&#xff0c;没有人能拯救我们&#xf…...

华为OD机试 - 磁盘容量(Python)| 真题+思路+考点+代码+岗位

磁盘容量 题目 磁盘的容量单位常用的有M、G、T 他们之间的换算关系为1T =1024G,1G=1024M 现在给定n块磁盘的容量,请对他们按从小到大的顺序进行稳定排序 例如给定5块盘的容量 5 1T 20M 3G 10G6T 3M12G9M 排序后的结果为 20M 3G 3M12G9M 1T 10G6T 注意单位可以重复出现 上述…...

更专业、安全、可控!政企都选择WorkPlus私有化部署

现如今政企机构在信息化建设的过程中&#xff0c;内部的沟通协作都离不开即时通讯软件。但大多数企业使用的即时通讯软件都是Saas部署的&#xff0c;虽然使用Saas部署产品成本低&#xff0c;又方便快捷&#xff0c;但还是建议企业有条件最好使用私有化部署的即时通讯软件&#…...

[SDX12] X12 USB to LTE IPA概率不生效问题分析及优化策略

问题描述 在测试USB to LTE的流量过程中,发现IPA概率失效,正常可以跑到320Mbps,但是跑流1分钟左右会出现IPA失效及跑流掉坑的情况。 问题log dmesg log 3,1862,149793394,-;ipa ipa3_ioctl:3564 using obselete command: IPA_IOC_RM_ADD_DEPENDENCY 3,1863,149793549,-;ipa …...

mysql8.0(单表查询与多表拆线)

目录 单表查询 1、显示所有职工的基本信息。 2、查询所有职工所属部门的部门号&#xff0c;不显示重复的部门号。 3、求出所有职工的人数。 4、列出最高工资和最低工资。 5、列出职工的平均工资和总工资。 6、创建一个只有职工号、姓名和工作时间的新表&…...

用于汽车传感器的混合点云语义压缩:性能评估

Hybrid Point Cloud Semantic Compression for Automotive Sensors: A Performance Evaluation https://arxiv.org/pdf/2103.03819.pdf 在自动驾驶中&#xff0c;车辆与车辆之间的信息共享起着重要作用。在所有传感器中&#xff0c;激光雷达产生的3D点云的数据量通常较高。因…...

那些企业网站做的较好/网络营销咨询公司

我用两台LinuxLinuxA IP&#xff1a;192.168.10.101LinuxB IP&#xff1a;192.168.10.102首先我们在LinuxA上挂载光驱和安装FTP服务器然后安装FTP服务器&#xff08;在同一台上&#xff0c;也就是LinuxA上&#xff09;修改FTP的主配置文件&#xff08;添加一句话anon…...

如何做网站访百度联盟/网站快速排名互点软件

在Stata/SE 16.0中&#xff0c;您可以使用以下命令将dta格式数据存储为Excel&#xff1a; export excel using filename.xlsx, replace其中&#xff0c;filename.xlsx是要存储的Excel文件的名称&#xff0c;replace选项指示如果该文件已经存在&#xff0c;则将其替换。 请注意&…...

wordpress 自定义函数/制作一个网站的全过程

cp -r /wenjian1/ /wenjian2/...

环保网站建设/北京网站建设公司报价

使用mybatis标签规避空where一、where标签案例1、原始sql2、简单if标签判断是否为空3、使用where标签后逻辑代码二、Select注解中当参数为空则不添加该参数的判断一、where标签案例 List<FilterUsersListQueryResDTO> filterUsers(Param("filterUsersQueryDTO"…...

唐山网站建设拓/宁德市

1、您认为做好测试用例设计工作的关键是什么&#xff1f; 白盒测试用例设计的关键是以较少的用例覆盖尽可能多的内部程序逻辑结果 黑盒法用例设计的关键同样也是以较少的用例覆盖模块输出和输入接口。不可能做到完全测试&#xff0c;以最少的用例在合理的时间内发现最多的问题…...

wordpress支付宝支付/手机网站智能建站

前言&#xff1a;很久之前就想动笔总结下关于软件设计的一些原则&#xff0c;或者说是设计模式的一些原则&#xff0c;奈何被各种bootstrap组件所吸引&#xff0c;一直抽不开身。关于设计模式&#xff0c;作为程序猿的我们肯定都不陌生。博主的理解&#xff0c;所谓设计模式就是…...