OpenCV实战(12)——图像滤波详解

news2024/11/19 1:55:08

OpenCV实战(12)——图像滤波详解

    • 0. 前言
    • 1. 频域分析
    • 2. 低通滤波器
    • 3. 图像下采样
      • 3.1 使用低通滤波器下采样图像
      • 3.2 内插像素值
    • 4. 中值滤波器
    • 5. 完整代码
    • 小结
    • 系列链接

0. 前言

滤波是信号和图像处理中的基本任务之一,其旨在有选择地提取图像的某些特征,可以用于在给定应用程序的上下文中传达重要信息,例如,去除图像中的噪声、提取所需的视觉特征、图像重采样等。图像滤波起源于信号系统理论,本节将介绍一些与滤波相关的重要概念,并展示如何在图像处理应用程序中使用滤波器。

1. 频域分析

首先,我们先简要说明频域分析 (frequency domain analysis) 的概念。不同图像具有不同的灰度分布,可以用图像的灰度分布作为表征图像的一种方式,但同时,还存在另一种分析图像的观点。观察图像中的灰度变化,可以发现,某些图像包含强度几乎恒定的大面积区域(例如,蓝天),而某些图像上的灰度强度变化很快(例如,拥挤的街道)。因此,可以用图像中的变化频率作为表征图像的另一种方式,这种方式被称为频域,而通过观察图像的灰度分布来表征图像的方式则被称为空间域。
频域分析将图像从最低频率到最高频率分解为其频率内容,图像强度缓慢变化的区域仅包含低频,而高频是由强度的快速变化产生的。有多种用于计算图像的频率内容的变换,例如傅立叶变换或余弦变换。需要注意的是,由于图像是二维平面,因此频率由垂直频率(垂直方向的变化)和水平频率(水平方向的变化)组成。
在频域分析中,滤波器是一种放大图像某些频带同时减少其他频带的操作。因此,低通滤波器 (low-pass filters) 是消除图像高频成分的滤波器,而高通滤波器 (high-pass filters) 消除图像的低频成分。本节将介绍一些在图像处理中经常使用的滤波器,并展示将它们应用于图像时的效果。

2. 低通滤波器

在本节中,我们将介绍一些基本的低通滤波器。我们已经了解了低通滤波器的目标是减少图像变化的幅度,实现此目标的一种简单方法是将每个像素替换为其周围像素的平均值,通过这种方式,快速的强度变化将变得平滑,从而被更平缓的过渡所取代。cv::blur 函数通过将每个像素替换为其矩形邻域上的平均像素值来平滑图像。

(1) 使用 5x5 的矩形作为滤波器调用平滑模糊函数,将像素值替换为其邻域的平均值:

cv::blur(image, result, cv::Size(5, 5));

这种滤波器也称为盒式滤波器 (box filter),我们使用 5x5 滤波器来调用 cv::blur() 函数,以使滤波器的效果更加明显。

原始图像

对上图应用滤波器可以得带以下结果:

均值滤波器

(2) 有时,我们可能更加关注像素邻域中较近的像素,此时我们可以使用加权平均值,邻居中靠近中心的像素比外围像素具有更大的权重。可以通过使用诸如高斯函数(钟形函数)的加权方案实现,OpenCV 中使用 cv::GaussianBlur 函数实现此滤波器:

cv::GaussianBlur(image, result, cv::Size(5, 5), 1.5);

滤波结果如下图所示:

高斯滤波器

如果滤波时用相邻像素的加权总和替换像素,则称该滤波器是线性的。均值滤波器就是一种线性滤波器,使用矩形邻域中所有像素的总和除以该邻域的大小后(以获得平均值)替换像素,这等价于在像素邻域中将每个像素乘以 1/n 并求和(其中 n 表示邻域大小),滤波器的不同权重可以使用矩阵表示,该矩阵包含与邻域中的每个像素位置相关联的乘法因子,矩阵的中心元素对应于当前应用滤波器的像素。这种矩阵也被称为核或掩码,3x3 均值滤波器的核如下:

[ 1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 ] \left[ \begin{array}{ccc} \frac 1 9 & \frac 1 9 & \frac 1 9\\ \frac 1 9 & \frac 1 9 & \frac 1 9\\ \frac 1 9 & \frac 1 9 & \frac 1 9\\\end{array}\right] 919191919191919191

cv::boxFilter 函数使用核元素全为 1 的核执行图像滤波,它类似于均值滤波器,但并未对求和结果计算均值。应用线性滤波器,在图像的每个像素上移动滤波器核并将每个对应的像素乘以其相关的权重,在数学上,这个操作称为卷积 (convolution),数学表达式如下:

I o u t ( x , y ) = ∑ i ∑ j I i n ( x − i , y − j ) K ( i , j ) I_{out}(x,y)=\sum_i\sum_jI_{in}(x-i,y-j)K(i,j) Iout(x,y)=ijIin(xi,yj)K(i,j)

上式中的二重求和将 ( x , y ) (x,y) (x,y) 处的当前像素与核 K K K 的中心对齐,假设其中心坐标为 ( 0 , 0 ) (0,0) (0,0)。查看生成的输出图像,可以观察到低通滤波器可以用于模糊或平滑图像,这是因为该滤波器会衰减图像中物体边缘快速变化产生的高频分量。
在高斯滤波器中,像素权重与其距中心像素的距离成正比。一维高斯函数表达式如下:

G ( x ) = A e − x 2 2 σ 2 G(x)=Ae^{\frac {-x^2} {2\sigma ^2}} G(x)=Ae2σ2x2

使用归一化系数 A A A 确保像素权重总和为 1 σ σ σ (sigma) 值控制高斯函数的宽度,该值越大,函数越平坦。例如,如果计算区间 [-4, 4] 的一维高斯滤波器的系数, σ = 0.5 σ = 0.5 σ=0.5,可以得到以下系数:

0.5 = [9.96126e-15 1.19795e-08 0.000263865 0.106451 0.786571 0.106451 0.000263865 1.19795e-08 9.96126e-15]

而当系数 σ = 1.5 σ=1.5 σ=1.5 时,系数如下:

1.5 = [0.00761442 0.036075 0.109586 0.213445 0.26656 0.213445 0.109586 0.036075 0.00761442]

这些值可以通过使用相应的 σ σ σ 值调用 cv::getGaussianKernel 函数获得:

cv::Mat gauss= cv::getGaussianKernel(9, sigma,CV_32F);

高斯函数的对称钟形使其成为滤波的良好选择:

高斯函数

离中心越远的像素权重越低,这使得像素间的过渡更平滑,而在简单的均值滤波器中,距离中心较远的像素会导致平均值的突变,从频率角度分析,这意味着均值滤波器不会去除所有高频分量。
要在图像上应用 2D 高斯滤波器,可以首先在图像横坐标方向上应用 1D 高斯滤波器(用于衰减水平频率),然后在图像纵坐标方向上应用另一个 1D 高斯滤波器(用于衰减垂直频率)。这是由于高斯滤波器是一个可分离滤波器,可分离滤波器意味着二维滤波器可以分解为两个一维滤波器,调用 cv::sepFilter2D 函数可以实现可分离滤波器的分离。也可以使用 cv::filter2D 函数直接应用二维核,一般来说,可分离滤波器比不可分离滤波器的计算速度更快,因为前者需要较少的乘法运算。
使用 cv::GaussianBlur() 函数需要提供滤波器中系数的数量(系数数量为奇数)作为函数的第 3 个参数,第 4 个参数 σ σ σ 指定所用高斯滤波器函数;也可以仅设置 σ σ σ 的值,且设置滤波器尺寸为 0,此时 OpenCV 可以自行确定适当的系数数量;或者可以仅设置滤波器尺寸,将 σ σ σ 值设置为 0,此时 OpenCV 将自行确定最适合给定尺寸的 σ σ σ 值。

3. 图像下采样

调整图像尺寸大小也称重采样 (resampled),是一种常见的图像处理操作。减小图像大小的过程通常称为下采样 (downsampling),而增加其大小的过程称为上采样 (upsampling)。执行这些操作的挑战在于确保尽可能保留图像的视觉质量。为了实现这一目标,通常使用低通滤波器 (low-pass filters)。
最简单的减小图像尺寸的方法是消除图像的某些列和行,但生成的图像质量并不好。下图通过通过简单地保留每 4 列(行)中的 1 列(行)而令原始图像大小缩小了 4 倍,可以看到图像质量大幅下降。为了便于观察图像的缺陷,我们通过放大像素显示来缩放图像:

图像缩放

可以看到图像质量有明显下降。例如,在图像的纹理部分(例如皮肤)上可以看到锯齿状扭曲。这些伪影是由空间混叠 (spatial aliasing) 现象引起的,当试图在一幅尺寸较小而无法包含高频分量的图像中显示高频分量时,会出现此问题,较小的图像(即像素较少的图像)无法像高分辨率图像那样表现出精细的纹理和锐利的边缘。由于图像中的精细细节对应于高频部分,我们需要在缩小图像尺寸之前消除图像中的高频分量,我们可以使用低通滤波器来完成。

3.1 使用低通滤波器下采样图像

(1) 要在不添加伪影的情况下将图像的大小缩小为原来的 1/4,必须先对原始图像应用低通滤波器,然后再丢弃图像中相应的像素列和行:

// 首先消除高频分量后缩放图像
cv::GaussianBlur(image, image, cv::Size(11, 11), 1.75);
cv::Mat reduced2(image.rows/4, image.cols/4, CV_8U);
for (int i=0; i<reduced2.rows; i++) {
    for (int j=0; j<reduced2.cols; j++) {
        reduced2.at<uchar>(i, j) = image.at<uchar>(i*4, j*4);
    }
}

生成的图像如下图所示:

低通滤波后缩放图像

图像中丢失了一部分精细细节,但总体而言,图像的视觉质量比直接丢弃像素更好。为了避免混叠效应,图像在缩小尺寸之前必须进行低通滤波,消除较小图像中无法表示的高频分量。这一理论通常被称为 Nyquist-Shannon 定理,简单而言,该理论证明了,如果将图像下采样为原来的 1/2,那么可表示频率的带宽也会减少 1/2
使用 OpenCV 函数 cv::pyrDown 可以执行图像缩小操作:

cv::Mat reducedImage; 
cv::pyrDown(image,reducedImage); 

以上函数使用 5x5 高斯滤波器对图像进行低通处理,然后将其缩小 2 倍,而函数 cv::pyrUp 则用于执行图像放大操作,上采样是通过在每行和列像素间插入 0 值,然后应用 5x5 高斯滤波器完成的。显然,如果在缩小图像后再放大,图像无法准确的恢复为原始图像,在缩小尺寸过程中丢失的图像细节无法恢复。
cv::pyrDowncv::pyrUp 函数可以用于创建图像金字塔,图像金字塔一个由同一图像的不同尺寸堆叠组成的数据结构(可以自定义尺寸的缩减因子),通常用于进行高效的图像分析。例如,如果想检测图像中的一个物体,可以先在金字塔顶部的小尺寸图像上完成检测,定位到目标物体后,可以通过移动到金字塔底部来细化搜索高分辨率版本的图像。
更通用的 cv::resize 函数可以指定所需的结果图像大小,只需通过指定新尺寸进行调用:

cv::Mat resizedImage;
cv::resize(image, resizedImage, cv::Size(image.cols/4, image.rows/4)); 

还可以指定比例因子调整图像大小,此时,指定一个空的 cv::Size 实例作为参数,然后传入比例因子:

cv::resize(image, resizedImage, cv::Size(), 1.0/4.0, 1.0/4.0); 

cv::resize 函数的最后一个参数用于选择要在重采样过程中使用的插值方法。

3.2 内插像素值

当图像按缩放因子调整大小时,需要执行一些像素插值,以便在现有像素之间填充新像素值。执行插值的最基本方法是使用最近邻策略,每个新像素都被赋值为原始图像中与其最接近像素的值。在图像上采样时,这意味着新图像的多个像素将从相同的原始像素接收值。例如,我们使用最近邻插值(插值标志 cv::INTER_NEAREST )将缩小后的图像重新放大 3 倍:

cv::resize(reduced, newImage, cv::Size(), 3, 3, cv::INTER_NEAREST);

结果如下图所示:

图像缩放

在这种情况下,插值相当于将每个像素的尺寸大小扩大为原来的 3 倍,为了获取更好的图像质量,我们可以通过组合几个相邻像素的值计算新的像素值,例如,我们可以通过考虑周围的四个像素线性插入新的像素值:

插值

首先在添加的像素的左侧和右侧垂直插入两个像素值,然后,这两个插值像素(上图中灰色圆形)用于查找插值位置所需的像素值。这种双线性插值方案是 cv::resize 使用的默认方法,可以由标志 cv::INTER_LINEAR 显式指定:

cv::resize(reduced2, newImage, cv::Size(), 4, 4, cv::INTER_LINEAR);

结果如下图所示:

双线性插值

也可以使用双三次插值以产生更好的结果,双三次插值使用 4x4 像素邻域执行插值,但由于该方法使用更多像素并使用三次项计算,因此它比双线性插值速度更慢。

4. 中值滤波器

上一小节中,我们已经介绍了线性滤波器的概念,此外,非线性滤波器也可以用于图像处理,中值滤波器就是一个非线性滤波器,中值滤波器对于消除椒盐噪声特别有效。

椒盐噪声

(1) 中值滤波函数的调用方式与其他滤波器类似,例如,使用核大小为 5cv::medianBlur 函数:

cv::medianBlur(image, result, 5);

生成的图像如下图所示:

中值滤波

由于中值滤波器不是线性滤波器,因此不能用核矩阵表示。但它也需要对像素的邻域进行操作以确定输出像素值。中值滤波器将计算当前像素及其邻域的中值,然后将当前像素替换为中值(一组变量值按大小顺序排列起来,形成一个数列,处于变量数列中间位置的变量值就称为中值,或称中位数)。
这也解释了中值滤波器在消除椒盐噪声方面的有效性:当给定像素邻域中存在异常黑色或白色像素时,它永远不会为中值(它是最大值或最小值),因此它总是被相邻值替换。相比之下,简单的均值滤波器会受到此类噪声的极大影响,如下图所示,使用均值滤波处理包含椒盐噪声的图像:

均值滤波

显然,噪声像素会改变相邻像素的平均值,即使噪声被均值滤波器所模糊,它仍然是可见的。
中值滤波器还具有保留边缘锐度的优点。但是,它会清除均匀区域中的纹理(例如,背景中的树木)。中值滤波器常用于在照片编辑软件工具中创建特殊效果,例如使用中值滤波器生成卡通化图像。

5. 完整代码

完整代码 (filters.cpp) 如下所示:

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

int main() {
    // 读取输入图像
    cv::Mat image = cv::imread("1.png", 0);
    if (!image.data) return 0;
    cv::namedWindow("Original Image");
    cv::imshow("Original Image", image);
    // 使用均值滤波器模糊图像
    cv::Mat result;
    cv::blur(image, result, cv::Size(5, 5));
    cv::namedWindow("Mean filtered Image");
    cv::imshow("Mean filtered Image", result);
    // 使用 9x9 均值滤波器模糊图像
    cv::blur(image, result, cv::Size(9, 9));
    cv::namedWindow("Mean filtered Image (9x9)");
    cv::imshow("Mean filtered Image (9x9)", result);
    // 高斯滤波
    cv::GaussianBlur(image, result, cv::Size(5, 5), 1.5);
    cv::namedWindow("Gaussian filtered Image");
    cv::imshow("Gaussian filtered Image", result);
    cv::GaussianBlur(image, result, cv::Size(9, 9), 1.7);
    cv::namedWindow("Gaussian filtered Image (9x9)");
    cv::imshow("Gaussian filtered Image (9x9)", result);
    // 获取高斯核
    cv::Mat gauss = cv::getGaussianKernel(9, 1.5, CV_32F);
    // 显示核值
    cv::Mat_<float>::const_iterator it = gauss.begin<float>();
    cv::Mat_<float>::const_iterator itend = gauss.end<float>();
    std::cout << "1.5 = [";
    for ( ; it!= itend; ++it) {
        std::cout << *it << " ";
    }
    std::cout << "]" << std::endl;
    // 获取高斯核 (sigmma=0.5)
    gauss = cv::getGaussianKernel(9, 0.5, CV_32F);
    it= gauss.begin<float>();  
    itend= gauss.end<float>();  
    std::cout << "0.5 = [";
    for ( ; it!= itend; ++it) {
        std::cout << *it << " ";
    }
    std::cout << "]" << std::endl;
    // 获取高斯核 (sigmma=2.5)
    gauss = cv::getGaussianKernel(9, 2.5, CV_32F);
    it= gauss.begin<float>();  
    itend= gauss.end<float>();  
    std::cout << "2.5 = [";
    for ( ; it!= itend; ++it) {
        std::cout << *it << " ";
    }
    std::cout << "]" << std::endl;
    // 获取高斯核 (9个元素)
    gauss = cv::getGaussianKernel(9, -1, CV_32F);
    it= gauss.begin<float>();  
    itend= gauss.end<float>();  
    std::cout << "9 = [";
    for ( ; it!= itend; ++it) {
        std::cout << *it << " ";
    }
    std::cout << "]" << std::endl;
    // 获取微分核 (2.5)
    cv::Mat kx, ky;
    cv::getDerivKernels(kx, ky, 2, 2, 7, true);
    cv::Mat_<float>::const_iterator kit= kx.begin<float>();  
    cv::Mat_<float>::const_iterator kitend= kx.end<float>();  
    std::cout << "[";
    for ( ; kit!= kitend; ++kit) {
        std::cout << *kit << " ";
    }
    std::cout << "]" << std::endl;
    // 读取带有椒盐噪声的图像
    image = cv::imread("noisy_img.png", 0);
    if (!image.data) return 0;
    cv::namedWindow("S&P Image");
    cv::imshow("S&P Image",image);
    // 使用均值滤波
    cv::blur(image, result, cv::Size(5, 5));
    cv::namedWindow("Mean filtered S&P Image");
    cv::imshow("Mean filtered S&P Image",result);
    // 使用中值滤波
    cv::medianBlur(image, result, 5);
    cv::namedWindow("Median filtered Image");
    cv::imshow("Median filtered Image",result);
    // 将图像缩小为原来的 1/4 (错误方式)
    image = cv::imread("1.png", 0);
    cv::Mat reduced(image.rows/4, image.cols/4, CV_8U);
    for (int i=0; i<reduced.rows; i++) {
        for (int j=0; j<reduced.cols; j++) {
            reduced.at<uchar>(i, j) = image.at<uchar>(i*4, j*4);
        }
    }
    cv::namedWindow("Badly reduced Image");
    cv::imshow("Badly reduced Image", reduced);
    cv::resize(reduced, reduced, cv::Size(), 4, 4, cv::INTER_NEAREST);
    cv::namedWindow("Badly reduced");
	cv::imshow("Badly reduced", reduced);
	cv::imwrite("badlyreducedimage.png", reduced);
    // 首先消除高频分量后缩放图像
    cv::GaussianBlur(image, image, cv::Size(11, 11), 1.75);
    cv::Mat reduced2(image.rows/4, image.cols/4, CV_8U);
    for (int i=0; i<reduced2.rows; i++) {
        for (int j=0; j<reduced2.cols; j++) {
            reduced2.at<uchar>(i, j) = image.at<uchar>(i*4, j*4);
        }
    }
    cv::namedWindow("Reduced Image, original size");
    cv::imshow("Reduced Image, original size", reduced2);
    cv::imwrite("reducedimage.png", reduced2);
    // 使用最近邻算法缩放图像
    cv::Mat newImage;
    cv::resize(reduced2, newImage, cv::Size(), 4, 4, cv::INTER_NEAREST);
    cv::namedWindow("Reduced Image");
    cv::imshow("Reduced Image",newImage);
    // 使用双线性插值缩放图像
    cv::resize(reduced2, newImage, cv::Size(), 4, 4, cv::INTER_LINEAR);
    cv::namedWindow("Bilinear resizing");
	cv::imshow("Bilinear resizing", newImage);
    // 创建图像金字塔
    cv::Mat pyramid(image.rows, 
            image.cols+image.cols/2+image.cols/4+image.cols/8,
            CV_8U, cv::Scalar(255));
    image.copyTo(pyramid(cv::Rect(0, 0, image.cols, image.rows)));
    cv::pyrDown(image, reduced);
    reduced.copyTo(pyramid(cv::Rect(image.cols, image.rows/2,
                                image.cols/2, image.rows/2)));
    cv::pyrDown(reduced, reduced2);
    reduced2.copyTo(pyramid(cv::Rect(image.cols+image.cols/2, 
                                image.rows-image.rows/4,
                                image.cols/4, image.rows/4)));
    cv::pyrDown(reduced2, reduced);
    reduced.copyTo(pyramid(cv::Rect(image.cols+image.cols/2+image.cols/4,
                                image.rows-image.rows/8,
                                image.cols/8, image.rows/8)));
    cv::namedWindow("Pyramid of images");
    cv::imshow("Pyramid of images", pyramid);
    cv::waitKey();
    return 0;
}

小结

图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,通常可以用作图像预处理操作,低通滤波器 (low-pass filters) 是消除图像高频成分的滤波器,而高通滤波器 (high-pass filters) 消除图像的低频成分。本节,我们介绍了频域分析的基本概念,并且介绍了常见的低通滤波器,同时介绍了常用的图像下采样方法,并展示了如何在图像处理应用程序中使用滤波器。

系列链接

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)——形态学变换详解

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

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

相关文章

【Linux】CentOS7操作系统安装nginx实战(多种方法,超详细)

文章目录前言一. 实验环境二. 使用yum安装nginx2.1 添加yum源2.1.1 使用官网提供的源地址&#xff08;方法一&#xff09;2.1.2 使用epel的方式进行安装&#xff08;方法二&#xff09;2.2 开始安装nginx2.3 启动并进行测试2.4 其他的一些用法&#xff1a;三. 编译方式安装ngin…

Angular快速入门

Angular1.框架背景2.Angular CLI2.1 安装2.2 主要特性2.3 创建module&#xff0c;component&#xff0c;service&#xff0c;class3.架构3.1 模块3.2 组件3.2.1 创建组件3.2.2 组件生命周期3.2.3 组件交互3.3 模板3.3.1 插值语法3.3.2 属性绑定3.3.3 条件判断3.3.4 循环语句3.3…

22. linux系统基础

递归遍历指定文件下所有的文件&#xff0c;而且你还可以统计一下普通文件的总个数&#xff0c;既然能统计普通文件&#xff0c;能统计其他文件吗&#xff1f;比如目录文件&#xff0c; 这个是main函数里面我们调用了 &#xff0c;这个checkdird这个函数&#xff0c;需要传递一个…

垃圾收集器以及三色标记

分代收集理论当前虚拟机的垃圾收集采用分代收集算法&#xff0c;只是根据对象存活周期的不同将内存分为几块。一般将java堆分为新生代和老年代&#xff0c;这样我们就可以根据各个年代的特点选择合适的垃圾收集算法新生代:每次收集都会有大量对象(近99%)死去&#xff0c;所以可…

教资教育知识与能力中学教学

目录 3.1 教学概述 3.2 教学过程 3.3 教学原则*【简答/辨析重点】 3.4 教学方法 3.5 教学组织形式 3.6 教学工作基本环节 3.7 教学评价 3.1 教学概述 1、教学的意义【14/18辨析】 教学是传授系统知识、促进学生发展的最有效形式&#xff1b; 教学是学校进行全面发展教…

MSTP多域实验配置

目录 实验配置 配置MSTP MSTP参数修改 查看每个设备的生成树 配置MSTP安全 实验配置 放行相关Vlan MST1域内的交换机互联接口放行Vlan 30、40 MST2域内的交换机互联接口放行Vlan 10、20、40 MST1域和MST2域之间交换机互联端口放行Vlan 40 配置MSTP 全网交换机都开启MSTP&a…

【SpringMVC】springMVC介绍

参考资料 视频资料 03_尚硅谷_SpringMVC_SpringMVC简介_哔哩哔哩_bilibili 笔记资料 第一节 SpringMVC概述 (wolai.com)链接&#xff1a;https://pan.baidu.com/s/1A7BX2TNfbGTpYene4x3Mew 提取码&#xff1a;a8d5 一、SpringMVC简介 1、什么是MVC MVC是一种软件架构的思…

React(五):受控组件、高阶组件、Portals、Fragment、CSS的编写方式

React&#xff08;五&#xff09;一、受控组件1.什么是受控组件&#xff08;v-model&#xff09;2.收集表单数据:input和单选框3.收集表单数据:下拉框二、非受控组件三、高阶组件1.什么是高阶组件2.高阶组件的应用13.高阶组件的应用2-注入Context4.高阶组件的应用3-登录鉴权5.高…

十一、GoF之代理模式

1 对代理模式的理解 【在程序中&#xff0c;对象A和对象B无法直接交互时。】 【在程序中&#xff0c;功能需要增强时。】 【在程序中&#xff0c;目标需要被保护时】 业务场景&#xff1a;系统中有A、B、C三个模块&#xff0c;使用这些模块的前提是需要用户登录&#xff0c;也…

HTML的表单标签

&#x1f31f;所属专栏&#xff1a;HTML只因变凤凰之路&#x1f414;作者简介&#xff1a;rchjr——五带信管菜只因一枚&#x1f62e;前言&#xff1a;该系列将持续更新HTML的相关学习笔记&#xff0c;欢迎和我一样的小白订阅&#xff0c;一起学习共同进步~&#x1f449;文章简…

将对象或数组存在 dom元素的属性上,最后取不到完整数据,只取到 [{

目录 一、问题 二、问题及解决方法 三、总结 一、问题 1.我需要在dom元素里面添加了一个属性test存一个对象数组temp&#xff0c;以便我下一次找到这个dom元素时可以直接拿到属性里面的数据来渲染页面。 2.dom 属性上存 对象和数组&#xff0c;必须先JSON.stringify(arr),转…

开发手册——一、编程规约_8.注释规约

这篇文章主要梳理了在java的实际开发过程中的编程规范问题。本篇文章主要借鉴于《阿里巴巴java开发手册终极版》 下面我们一起来看一下吧。 1. 【强制】类、类属性、类方法的注释必须使用 Javadoc 规范&#xff0c;使用 /**内容*/ 格式&#xff0c;不得使用 // xxx 方式。 说…

C++ 线程库

文章目录thread 创建mutexmutexrecursive_mutextimed_mutexlock_guard原子操作atomic条件变量condition_variable其他线程安全问题shared_ptr单例模式C 线程库是 C11 标准中引入的一个特性&#xff0c;它使得 C 在语言级别上支持多线程编程&#xff0c;不需要依赖第三方库或操作…

unity开发知识点小结01

unity对象生命周期函数 Awake():最早调用&#xff0c;所以可以实现单例模式 OnEnable&#xff08;&#xff09;&#xff1a;组件激活后调用&#xff0c;在Awake后调用一次 Stat&#xff08;&#xff09;&#xff1a;在Update&#xff08;&#xff09;之前&#xff0c;OnEnable…

【C++知识点】位运算

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;C/C知识点 &#x1f4e3;专栏定位&#xff1a;整理一下 C 相关的知识点&#xff0c;供大家学习参考~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;…

海思嵌入式开发-005-OpenHarmony源码编译问题

海思嵌入式开发-005-OpenHarmony源码编译问题一、问题描述二、解决方案2.1解决原理2.2获取OpenHarmony 3.1.1 Release源码2.3最后解决问题&#xff0c;编译成功。一、问题描述 按照链接拉取master源码&#xff0c;出现如下问题&#xff0c;打开build.log文件 提示相应位置的文…

Servlet详细教程

文章目录Servletservlet 简介Servlet 入门案例页面编写页面提交 get 请求Servlet 和 Tomcat 关系servlet-apiget 和 post 请求Servlet 生命周期案例HttpServletRequest 接口简介文件上传FileServlet 类Servlet servlet 简介 servlet 全称为 server applet 是服务器的小程序&am…

龙腾iSharedisk无盘系统 v1.8 Build 20230207 Crack

龙腾 iShareDisk无盘系统是一款高品质的 无盘启动和VHD离线启动系统。其功能满足目前校园、网咖、企业、酒店、证券、服务业、KTV、包厢VOD的需求&#xff0c;其可以 自行选择部署有盘或者无盘&#xff0c;实现Windows全系列产品无盘/VHD 启动的一体化解决方案&#xff01; …

【Storm】【七】Storm三种打包方式对比分析

Storm三种打包方式对比分析 一、简介二、mvn package三、maven-assembly-plugin插件四、maven-shade-plugin插件五、结论六、打包注意事项一、简介 在将 Storm Topology 提交到服务器集群运行时&#xff0c;需要先将项目进行打包。本文主要对比分析各种打包方式&#xff0c;并…

MyBatis - 14 - 分页插件的配置及使用

文章目录1、分页插件配置&#xff08;1&#xff09;在pom.xml中添加依赖&#xff08;2&#xff09;在MyBatis的核心配置文件中配置插件2、分页插件的使用回顾Mysql分页功能MyBatis分页插件的使用测试显示第1页&#xff0c;每页显示4条数据&#xff0c;打印page对象测试获取分页…