图像处理之空间滤波

news/2024/7/7 21:31:22

1 原理

1.1 空间滤波简介

  滤波器即只让一部分频率的波形通过来达到波形过滤目的的器件。空间域指一张图像像素平面一定范围内的像素域,相对的是时间域,即多帧图像之间的关系,主要在处理视频帧时描述。在图像处理中,滤波分为两种:

  • 频域滤波,将图像转换到频域进行相应的滤波;
  • 空间滤波,更准确的说法是掩膜,即在图像的像素平面上利用空间滤波器对图像进行处理。

  空间滤波器是一个 m × n m\times n m×n大小的矩阵(通常为 3 × 3 3\times 3 3×3,一般都为奇数,偶数核无法完全对齐,需要进行但方向上的padding就会产生图像移位的问题),矩阵定义了希望对当前像素邻域执行的操作和具体的参数,该滤波器会掠过待处理图像的每个像素点来达到,得到对应的输出图像。空间滤波器根据滤波器的特性分为:

  • 线性空间滤波:即对邻域内像素的操作为线性。比如当前像素的输出为滤波器与邻域像素点乘积之和;
  • 非线性空间滤波:即对邻域内像素的操作为非线性。比如输出像素只取当前像素邻域的最大值。

  比如,对于输入为 M × N M\times N M×N的图像,滤波器的核大小为 m × n m\times n m×n的线性滤波的数学表示为:
g ( x , y ) = ∑ i = − a a ∑ j = − b b w ( i , j ) f ( x + i , y + j ) g(x,y)=\sum_{i=-a}^{a}\sum_{j=-b}^{b}w(i,j)f(x+i,y+j) g(x,y)=i=aaj=bbw(i,j)f(x+i,y+j)
  其中 g ( x , y ) g(x,y) g(x,y)为输出像素,区间 [ − a , − b ] [-a,-b] [a,b]的长度为 m m m,区间 [ − n , n ] [-n,n] [n,n]的长度为 n n n w ( i , j ) w(i,j) w(i,j)为对应 i , j i,j i,j位置的滤波器的权重参数。
  在进行空间滤波时如果不对原图像进行填充(padding)的话输出的图像的大小就是 ( M − m + 1 ) × ( N − n + 1 ) (M - m + 1)\times(N - n + 1) (Mm+1)×(Nn+1),如果希望滤波器的中心能够掠过每个像素或者得到和输入大小箱通风的图像就需要对原图进行填充。填充的方式根据具体的需要来定,最为常用的为在边界上填充固定像素,如果 m ≠ n m\ne n m=n时,则水平和垂直方向padding的像素值不同。当然也可以在原图像素间填充固定像素值。

1.2 空间相关和卷积

  线性空间滤波中相关和卷积的概念相近:

  • 相关:滤波器掠过图像中每个像素计算乘积之和;
  • 卷积:首先将滤波器旋转 18 0 。 180^{。} 180滤波器略过图像中每个像素计算每个像素的乘积之和。

  二者的区别是,假如输入图像是单位冲激图像,则相关得到的是滤波器输入的180度旋转的值;而卷积得到的是原滤波器的参数值。

2 不同类型的滤波器

2.1 均值滤波器

  均值滤波器,顾名思义,对邻域邻域内像素取均值,属于低通滤波器。均值滤波器可以将预期内的像素值平均化,降低图像中原来比较尖锐的部分抑制图像的高频。均值滤波的数学表达比较简单:
g ( x , y ) = ∑ s = − a a ∑ t = − b b w ( s , t ) f ( x + s , y + t ) ∑ s = − a a ∑ t = − b b w ( s , t ) g(x,y)=\frac{\sum_{s=-a}^{a}\sum_{t=-b}^{b}w(s,t)f(x+s,y+t)}{\sum_{s=-a}^{a}\sum_{t=-b}^{b}w(s,t)} g(x,y)=s=aat=bbw(s,t)s=aat=bbw(s,t)f(x+s,y+t)
  公式中, w , f , g w,f,g w,f,g分别为滤波器参数,输入像素,输出像素。输出像素的值为领域内每个像素的像素值和滤波器权重逐一相乘然后求和再除以滤波器权重之和求平均。
  影响滤波器的效果有两部分,滤波器的参数和滤波器的大小。滤波器的核越大所能作用的邻域越大,均质化的效果越明显。下图为核尺寸从 [ 3 , 33 ] [3,33] [3,33]步进为2,权重参数为全1的效果。
在这里插入图片描述

  如果均值滤波器的权重参数全部为1就是盒装滤波器。下面两个矩阵左边是盒装滤波器的参数,右边是根据像素距离作为权重的矩阵。
[ 1 1 1 1 1 1 1 1 1 ] [ 1 2 1 2 4 2 1 2 1 ] \begin{bmatrix} 1&1&1\\ 1&1&1\\ 1&1&1\\ \end{bmatrix} \begin{bmatrix} 1&2&1\\ 2&4&2\\ 1&2&1\\ \end{bmatrix} 111111111 121242121

  在实践中,由于这些模板在一幅图像中跨过的区域很小,即便权重参数不同,通常也很难看出差异。——《数字图像处理》

在这里插入图片描述

  另外需要注意的是,从上面的图像中能够明显的看出边缘有黑边,而且滤波器的核越大越明显。原因很简单就是因为边缘填充的是0导致的,实际处理中只需要填充最近的像素值即可。

static Mat avgFilterTransformOneChannel(const Mat &gray, const Mat &weight) {
    int weiHeight = weight.rows, weiWidth = weight.cols;
    assert( gray.channels() == 1 && weight.channels() == 1 && weiHeight % 2 == 1 && weiWidth % 2 == 1);
    double wsum = cv::sum(weight)[0];
    //填充padding像素保证每个像素都能被滤波器掠过
    Mat paddingMat, retMat(cv::Size(gray.rows, gray.cols), CV_8UC1);
    int horPaddPixels = floor(weiHeight / 2), verPaddPixels = floor(weiHeight / 2);
    copyMakeBorder(gray, paddingMat, verPaddPixels, verPaddPixels, horPaddPixels, horPaddPixels, BORDER_CONSTANT, Scalar(0));
    unsigned int imgWidth = gray.cols, imgHeight = gray.rows;
    for (int i = 0; i < imgHeight; i++) {
        for (int j = 0; j < imgWidth; j++) {
            Mat currentRoi = paddingMat(cv::Range(i, i + 2 * horPaddPixels + 1), cv::Range(j, j + 2 * verPaddPixels + 1)), multMat;
            cv::multiply(weight, currentRoi, multMat);
            double currentValue = sum(multMat)[0] / (wsum * 1.0);
            retMat.at<uchar>(i, j) = floor(currentValue);
        }
    }

    return retMat;
}
Mat GrayTransform::avgFilterTransform(const cv::Mat &img, const cv::Mat &weight) {
    std::vector<Mat> chanelMats;
    split(img, chanelMats);
    for (int i = 0; i < chanelMats.size(); i++) {
        chanelMats[i] = avgFilterTransformOneChannel(chanelMats[i], weight);
    }

    Mat returnMat;
    merge(chanelMats, returnMat);
    return returnMat;
}

2.2 统计排序滤波

  统计排序滤波就是利用邻域内像素的统计特性的一种非线性滤波。比如输出的像素取邻域内像素最大值的最大值滤波,取最小值的最小值滤波,取中值的中值滤波。
  下图从左到右分别为:原图,最大值滤波,最小值滤波,中值滤波。
在这里插入图片描述

  中值滤波可以用来消除图像中的椒盐噪声。

static double matMaxValue(const Mat &img) {
    assert(img.elemSize() == sizeof(uchar));
    std::vector<uchar> vec(img.begin<uchar>(), img.end<uchar>());
    std::sort(vec.begin(), vec.end());
    return vec.at(vec.size() - 1);
}

static double matMinValue(const Mat &img) {
    assert(img.elemSize() == sizeof(uchar));
    std::vector<uchar> vec(img.begin<uchar>(), img.end<uchar>());
    std::sort(vec.begin(), vec.end());
    return vec.at(0);
}

static double matMedValue(const Mat &img) {
    assert(img.elemSize() == sizeof(uchar));
    std::vector<uchar> vec(img.begin<uchar>(), img.end<uchar>());
    std::sort(vec.begin(), vec.end());
    return vec.size() / 2 == 1 ? vec[int(vec.size() / 2)] : (vec[int(vec.size() / 2)] + vec[int(vec.size() / 2) + 1]) / 2.0;
}

using StaticFilterFunc = double (*)(const Mat &area);
static Mat statsticFilterTransformOneChannel(const cv::Mat gray, StaticFilterFunc func, const Size &filterSize) {
    int weiHeight = filterSize.height, weiWidth = filterSize.width;
    assert(gray.channels() == 1 && weiHeight % 2 == 1 && weiWidth % 2 == 1);
    //填充padding像素保证每个像素都能被滤波器掠过
    Mat paddingMat, retMat(cv::Size(gray.rows, gray.cols), CV_8UC1);
    int horPaddPixels = floor(weiHeight / 2), verPaddPixels = floor(weiHeight / 2);
    copyMakeBorder(gray, paddingMat, verPaddPixels, verPaddPixels, horPaddPixels, horPaddPixels, BORDER_CONSTANT, Scalar(0));
    unsigned int imgWidth = gray.cols, imgHeight = gray.rows;
    for (int i = 0; i < imgHeight; i++) {
        for (int j = 0; j < imgWidth; j++) {
            Mat currentRoi = paddingMat(cv::Range(i, i + 2 * horPaddPixels + 1), cv::Range(j, j + 2 * verPaddPixels + 1)), multMat;
            double currentValue = func(currentRoi);
            retMat.at<uchar>(i, j) = floor(currentValue);
        }
    }

    return retMat;
}

Mat GrayTransform::statsticFilterTransform(const cv::Mat &img, const FilterStatsticType type, const Size &filterSize) {
    std::vector<Mat> chanelMats;
    split(img, chanelMats);
    StaticFilterFunc func;
    switch (type)
    {
    case FILTER_STATSTIC_MIN:
        func = matMinValue;
        break;
    case FILTER_STATSTIC_MAX:
        func = matMaxValue;
        break;
    case FILTER_STATSTIC_MED:
        func = matMedValue;
        break;
    default:
        abort();
    }

    for (int i = 0; i < chanelMats.size(); i++) {
        chanelMats[i] = statsticFilterTransformOneChannel(chanelMats[i], func, filterSize);
    }

    Mat returnMat;
    merge(chanelMats, returnMat);
    return returnMat;
}

2.3 锐化空间滤波器

  如果将卷积相关看做是对邻域内像素和权重进行积分的话,那么相对的说对应像素的微分也可以用用于滤波处理。
  在数学上,微分最直接的含义就是 d f d x = lim ⁡ x → 0 f ( x + σ ) − f ( x ) σ \frac{\mathrm{d}f}{\mathrm{d}x}={\lim_{x\to 0}\frac{f(x + \sigma) - f(x)}{\sigma}} dxdf=limx0σf(x+σ)f(x)。但是在数字图像处理中数据都是离散的,数字量都是有限的,且变换都发生在两个像素之间,因此根据一阶导数的定义,图像上近似的一阶偏微分为:
∂ f ∂ x = f ( x + 1 , y ) − f ( x , y ) ∂ f ∂ y = f ( x , y + 1 ) − f ( x , y ) \begin{equation} \begin{aligned} \frac{\partial{f}}{\partial{x}}&=f(x+1,y) - f(x,y)\\ \frac{\partial{f}}{\partial{y}}&=f(x,y+1) - f(x,y) \end{aligned} \end{equation} xfyf=f(x+1,y)f(x,y)=f(x,y+1)f(x,y)

  二阶偏导,我们很容易能够推导出:

∂ 2 f ∂ x 2 = f ( x + 1 , y ) + f ( x − 1 ) − 2 f ( x , y ) ∂ 2 f ∂ y 2 = f ( x , y + 1 ) + f ( x , y − 1 ) − 2 f ( x , y ) \begin{equation} \begin{aligned} \frac{\partial^2{f}}{\partial{x^2}}&=f(x+1,y)+f(x-1)-2f(x,y)\\ \frac{\partial^2{f}}{\partial{y^2}}&=f(x,y+1)+f(x,y-1)-2f(x,y)\\ \end{aligned} \end{equation} x22fy22f=f(x+1,y)+f(x1)2f(x,y)=f(x,y+1)+f(x,y1)2f(x,y)

  数字图像中,对于有边缘的区域,一阶导数很容易形成一个比较粗略的边缘,而二阶导数可以产生由0分开的一个像素宽的双边缘。因此,二阶微分在增强图形的细节上要比一阶微分要好。下图原图在为 x x x y y y方向上的一阶微分和二阶微分结果,可以明显的看到一阶微分的边缘要比二阶微分宽(从左到右分别为原图,x方向上的一阶微分,x方向上的二阶微分,y方向上的一阶微分,y方向上的二阶微分)
在这里插入图片描述

2.3.1 拉普拉斯算子

  拉普拉斯算子是一个各向同性算子,滤波器的响应和滤波器作用的图像的方向无关,即即便图像旋转后进行滤波和滤波后旋转效果相同。拉普拉斯算子的定义为:
▽ 2 f = ∂ 2 f ∂ x 2 + ∂ 2 f ∂ y 2 \bigtriangledown^2 f=\frac{\partial^2 f}{\partial x^2}+\frac{\partial^2 f}{\partial y^2} 2f=x22f+y22f
  根据上面求得的二阶微分,将其应用到离散图像上:
▽ 2 f ( x , y ) = f ( x + 1 , y ) + f ( x − 1 , y ) + f ( x , y − 1 ) + f ( x , y + 1 ) − 4 f ( x , y ) \bigtriangledown^2f(x,y)=f(x+1,y)+f(x-1,y)+f(x,y-1)+f(x,y+1)-4f(x,y) 2f(x,y)=f(x+1,y)+f(x1,y)+f(x,y1)+f(x,y+1)4f(x,y)
  上面的公式描述了以 9 0 。 90^{。} 90为增量进行旋转的各向同性拉普拉斯算子,如果希望扩展到 4 5 。 45^{。} 45只需要将45度方向上的二阶偏导引入即可:
▽ 2 f ( x , y ) = f ( x + 1 , y ) + f ( x − 1 , y ) + f ( x , y − 1 ) + f ( x , y + 1 ) + f ( x − 1 , y − 1 ) + f ( x − 1 , y + 1 ) + f ( x + 1 , y − 1 ) + f ( x + 1 , y + 1 ) − 8 f ( x , y ) \bigtriangledown^2f(x,y)=f(x+1,y)+f(x-1,y)+f(x,y-1)+f(x,y+1)+f(x-1,y-1)+f(x-1,y+1)+f(x+1,y-1)+f(x+1,y+1)-8f(x,y) 2f(x,y)=f(x+1,y)+f(x1,y)+f(x,y1)+f(x,y+1)+f(x1,y1)+f(x1,y+1)+f(x+1,y1)+f(x+1,y+1)8f(x,y)
  应用到空间滤波中,对应的矩阵参数分别为
[ 0 1 0 1 − 4 1 0 1 0 ] [ 1 1 1 1 − 8 1 1 1 1 ] \begin{bmatrix} 0&1&0\\ 1&-4&1\\ 0&1&0\\ \end{bmatrix} \begin{bmatrix} 1&1&1\\ 1&-8&1\\ 1&1&1\\ \end{bmatrix} 010141010 111181111
  通过上面的方式我们能够得图像的边缘,直接将计算出的结果通过下面的公式和原图相加即可。
g ( x , y ) = f ( x , y ) + c ▽ 2 f ( x , y ) g(x,y)=f(x,y)+c\bigtriangledown^2f(x,y) g(x,y)=f(x,y)+c2f(x,y)
  下图中展示了两种拉普拉斯空间滤波的结果,上一张图是90度各向同性的结果,下面的图示45度各向同性的结果。(下图中从左到右为1,2,3,4四张图,分别为原图,拉普拉斯变化值,标定后的拉普拉斯变化值,和原图线性相加后的值c=-1)
在这里插入图片描述
在这里插入图片描述

  下图为参数c对两种矩阵的影响。

//标定,实际上就是将计算出来的值映射到0-255
static Mat stardandOneChannel(const cv::Mat &gray) {
    Mat fltMat;
    Mat returnMat(gray.rows, gray.cols, CV_32FC1);
    constexpr int layer = 255;
    gray.convertTo(fltMat, CV_64FC1);
    double maxValue = matMaxValue<double>(fltMat);
    double minValue = matMinValue<double>(fltMat);
    for (int i = 0; i < fltMat.rows; i++) {
        for (int j = 0; j < fltMat.cols; j++) {
            double value = (fltMat.at<double>(i, j) - minValue * 1.0) / (maxValue - minValue) * layer;
            returnMat.at<float>(i, j) = static_cast<float>(value);
        }
    }

    return returnMat;
}

Mat GrayTransform::standardMat(const cv::Mat &img) {
    std::vector<Mat> chanelMats;
    split(img, chanelMats);
    for (int i = 0; i < chanelMats.size(); i++) {
        chanelMats[i] = stardandOneChannel(chanelMats[i]);
    }

    Mat returnMat;
    merge(chanelMats, returnMat);
    return returnMat;
}

2.3.2 非锐化掩蔽和高提升滤波

  非锐化掩蔽是利用模糊图像和原图之间的差值模板来突出原图中的边缘,然后将得到的模板加到原图来突出边缘达到锐化的目的。
g m a s k ( x , y ) = f ( x , y ) − f ‾ ( x , y ) g ( x , y ) = f ( x , y ) + k g m a s k ( x , y ) \begin{equation} \begin{aligned} g_{mask}(x,y)&=f(x,y)-\overline{f}(x,y)\\ g(x,y)&=f(x,y)+k g_{mask}(x,y) \end{aligned} \end{equation} gmask(x,y)g(x,y)=f(x,y)f(x,y)=f(x,y)+kgmask(x,y)
  其中 f ‾ ( x , y ) \overline{f}(x,y) f(x,y)为经过空间滤波的模糊图像, k k k用来控制最终希望得到的图像的锐化程度。

  • k < 1 k<1 k<1,则不强调非锐化模板的影响;
  • k = 1 k=1 k=1,为非锐化掩蔽;
  • k > 1 k>1 k>1,为高提升滤波。

  下图从左到右分别为:原图,均值模糊,差值,k=0.5,k=1,k=2。
在这里插入图片描述

2.3.3 一阶微分图像锐化

  对于一张图像有多个方向,一般很容易求得 x , y x,y xy两个方向的偏导 g x = ∂ f ∂ x g_x=\frac{\partial{f}}{\partial{x}} gx=xf和g_y= ∂ f ∂ y \frac{\partial{f}}{\partial{y}} yf,该偏导只表示了当前点在对应方向上的变化程度。而值 M ( x , y ) = m a g ( ▽ ) = g x 2 + g y 2 M(x,y)=mag(\bigtriangledown)=\sqrt{g_x^2+g_y^2} M(x,y)=mag()=gx2+gy2 表示了该点的梯度。再具体实现中可用 M ( x , y ) = ∣ g x ∣ + ∣ g y ∣ M(x,y)=|g_x|+|g_y| M(x,y)=gx+gy求得梯度的近似。
  罗伯特交叉梯度算子利用当前点的对角值计算梯度,能够比直接在x或者y方向上计算梯度多近似0.5个像素的偏移,且 g x , g y g_x,g_y gx,gy都包含了x和y方向上的梯度信息。罗伯特交叉梯度算子中:
g x ( x , y ) = f ( x + 1 , y + 1 ) − f ( x , y ) g y ( x , y ) = f ( x , y + 1 ) − f ( x + 1 , y ) M ( x , y ) ≈ ∣ f ( x + 1 , y + 1 ) − f ( x , y ) ∣ + ∣ f ( x , y + 1 ) − f ( x + 1 , y ) ∣ \begin{equation} \begin{aligned} g_x(x,y)&=f(x+1,y+1)-f(x,y)\\ g_y(x,y)&=f(x,y+1)-f(x+1,y)\\ M(x,y)&\approx |f(x+1,y+1)-f(x,y)|+|f(x,y+1)-f(x+1,y)| \end{aligned} \end{equation} gx(x,y)gy(x,y)M(x,y)=f(x+1,y+1)f(x,y)=f(x,y+1)f(x+1,y)f(x+1,y+1)f(x,y)+f(x,y+1)f(x+1,y)
  对应的算子矩阵为
[ − 1 0 0 1 ] [ 0 − 1 1 0 ] \begin{bmatrix} -1&0\\ 0&1 \end{bmatrix} \begin{bmatrix} 0&-1\\ 1&0 \end{bmatrix} [1001][0110]

  罗伯特交叉梯度算子为偶数算子没有对称中心,不容易实现。Soble算子采用一阶微分,奇数核。其中第二个权重系数为2是为了突出中心点来平滑图像。
g x ( x , y ) = ( f ( x + 1 , y − 1 ) + 2 f ( x + 1 , y ) + f ( x + 1 , y − 1 ) ) − ( f ( x − 1 , y − 1 ) + 2 f ( x − 1 , y ) + f ( x − 1 , y − 1 ) ) g y ( x , y ) = ( f ( x + 1 , y + 1 ) + 2 f ( x , y + 1 ) + f ( x − 1 , y + 1 ) ) − ( f ( x + 1 , y − 1 ) + 2 f ( x , y − 1 ) + f ( x − 1 , y − 1 ) ) M ( x , y ) ≈ ∣ g x ∣ + ∣ g y ∣ \begin{equation} \begin{aligned} g_x(x,y)&=(f(x+1,y-1)+2f(x+1,y)+f(x+1,y-1))-(f(x-1,y-1)+2f(x-1,y)+f(x-1,y-1))\\ g_y(x,y)&=(f(x+1,y+1)+2f(x,y+1)+f(x-1,y+1))-(f(x+1,y-1)+2f(x,y-1)+f(x-1,y-1))\\ M(x,y)&\approx |g_x|+|g_y| \end{aligned} \end{equation} gx(x,y)gy(x,y)M(x,y)=(f(x+1,y1)+2f(x+1,y)+f(x+1,y1))(f(x1,y1)+2f(x1,y)+f(x1,y1))=(f(x+1,y+1)+2f(x,y+1)+f(x1,y+1))(f(x+1,y1)+2f(x,y1)+f(x1,y1))gx+gy
  对应的矩阵为
g x = [ − 1 0 1 − 2 0 2 − 1 0 1 ] g y = [ − 1 − 2 − 1 0 0 0 1 2 1 ] g_x= \begin{bmatrix} -1&0&1\\ -2&0&2\\ -1&0&1\\ \end{bmatrix} g_y= \begin{bmatrix} -1&-2&-1\\ 0&0&0\\ 1&2&1\\ \end{bmatrix} gx= 121000121 gy= 101202101

  下图分别为原图, g x g_x gx, g y g_y gy, 0.3 M ( x , y ) 0.3M(x,y) 0.3M(x,y),处理后的图。
在这里插入图片描述


http://lihuaxi.xjx100.cn/news/55059.html

相关文章

PTA - 数据库合集6(10题)

目录 10-1 查询选修‘C语言’课程的学生 10-2 查询平均分高于80分的学生 10-3 查询平均成绩最高的前3名同学 10-4 批量插入学生记录 10-5 修改女生成绩 10-7 spj-查询供应工程 j1 的供应商 10-8 spj-查询至少使用s1供应商所供应的全部零件的工程 10-9 查询年龄18-20之间…

Linux--权限管理

学习目标1. Linux权限管理1.1 用户分类2. 用户类型和访问权限2.1 理解什么是权限3 文件类型和权限操作3.1 修改权限3.2 关于root3.3 更改文件拥有者3.4 修改组权限3.5 目录权限3.5.1 粘滞位3.6 关于目录权限的总结3.7 默认权限3.7.1 自定义默认权限1. Linux权限管理 1.1 用户分…

Kubernetes(k8s)基础之三:K8s常用命令

目录 kubectl的基本命令 k8s常用操作命令 k8s常用命令操作示例 资源管理 命令式对象管理 kubectl命令 资源类型 查看k8s对象状态 k8s对象配置 k8s容器编排配置文件模板 1. deployment 相关使用 创建deployment 更新deployment 回退deployment 暂停和恢复deploym…

基于PHP+小程序(MINA框架)+Mysql数据库的积分商城小程序系统设计与实现

项目背景和意义 目的&#xff1a;本课题主要目标是设计并能够实现一个基于微信小程序商城系统&#xff0c;前台用户使用小程序&#xff0c;小程序使用微信开发者工具开发&#xff1b;后台管理使用基PPMySql的B/S架构&#xff0c;开发工具使用phpstorm&#xff1b;通过后台添加商…

Teleport 内置组件 (传送门)

<Teleport> 接收一个 to prop 来指定传送的目标。to 的值可以是一个 CSS 选择器字符串&#xff0c;也可以是一个 DOM 元素对象。这段代码的作用就是告诉 Vue“把以下模板片段传送到 body 标签下”。 <button click"open true">Open Modal</button&g…

Chapter9.2:线性系统的状态空间分析与综合

本系列属于胡寿松《自动控制原理》(第七版)教材的课后习题精选,需要完整版课后习题答案的同学,请自行查找,本系列基本包含了自动控制原理的知识点,搭配胡寿松《自动控制原理》(第七版)知识点提炼使用,可用于期末考试甚至考研复习。 《自动控制原理》(第七版)知识点提炼 Ex…

Java 多线程概念

线程和进程区别 进程是程序执行的过程&#xff0c;线程是轻量级进程&#xff0c;程序执行的分支 进程是资源分配单位&#xff0c;线程是资源调度单位;线程是进程划分成的更小的运行单位&#xff1b;进程相互独立&#xff0c;同一进程中的线程可能会相互影响&#xff1b;线程执…

5.4 堆溢出利用(上)——DWORD SHOOT

目录 一、预备知识 二、实验环境 三、实验代码 四、实验步骤 一、预备知识 堆管理系统的三类操作&#xff1a;堆块分配、堆块释放和堆快合并归根结底都是对链表的修改。堆溢出利用的精髓就是用精心构造的数据溢出下一个堆块的块首&#xff0c;改写块首中的前向指针&#xf…