Harris/Shi-Tomasi角点检测
机器视觉——角点检测
什么是角点检测
在几何学里,我们会看到各种各样的三角形、多边形等,它们都有一个显著的特征:包含了角点信息。比如在三角形里,我们有三个角;在矩形里,我们有四个角。我们将找到这些图像特征的过程称为特征提取 (Feature Extraction),我们之前所接触的Canny边缘检测也是特征提取的一种,其提取的是边缘特征,而我们这次所需要的是提取角点特征。现在,我们有一张图片需要去处理,要求:将图中书本的四个角点提取出来,以方便后续做透视变换等操作。这应该怎么样处理?
我们先对图像进行预处理操作
通过前面一系列知识的学习,我们可以将该RGB图片转换成灰度图像(本人比较懒,不想写代码,就用ps做处理了):
随后,我们对该图像进行二值化操作:
可以看到该图像周围还是有很多的噪声,于是我们对图像进行形态学操作+中值滤波:
这样,我们的图像预处理工作就完成了,接下来是提取角点信息。我们通常有两种方法:Harris角点检测与Shi-Tomasi角点检测
Harris角点检测
我们怎么样去提取一张图像里的角点信息呢?我们可以使用一个滑动窗口对图像进行操作,如下图所示:
当窗口在图像上滑动时,我们可以根据窗口所在的区域分为如图的三种情况:
- 平坦区域:窗口内像素值几乎没有变化
- 边缘区域:沿平行于边缘的方向移动,像素值不会变化
- 角点区域:不管朝哪个方向移动,像素值都会发生变化‘
所以说,引起窗口内较大像素值变化的地方就是我们想要找寻的角点。那么我们该怎么样去找寻这些地方呢?
我们让一个窗口的中心位于灰度图像的一个位置\((x,y)\),这个位置的像素灰度值为\(I(x,y)\),如果这个窗口分别向x和y方向移动一个小的位移\(\Delta x\)和\(\Delta y\),到一个新的位置\((x+\Delta x, y+\Delta y)\),这个位置的像素灰度值就是\(I(x+\Delta x, y+\Delta y)\),那么我们所说的灰度变化值就是\([I(x+\Delta x, y+\Delta y)-I(x,y)]\)。我们将上式做个平方处理,因为灰度的变化有正有负;然后再给每个像素乘上一个权值\(w(x,y)\),表示图像在\((x,y)\)处像素点的权值,而这个\(w(x,y)\)也就是我们说的窗口函数。最简单的,我们可以把窗口内的权重都设置为1,窗口大小一般设置为3x3以上(根据实际情况而定);有时我们也会将窗口内的像素权值设为一个高斯分布(二元正态分布)等(因为当窗口在角点位置移动时,中心点像素变化最大,所以将中心点的权值设为最大,周围逐渐下降,也就是一个高斯分布)。
这样,我们可以构造出一个函数,用来描述窗口在图像上移动时,窗口内像素灰度值的变化,该函数如下:
其中:\\
W为窗口,(x,y)\subset W的意思也就是(x,y)可以取遍窗口内包含的所有像素 \\w(x,y)就相当于一个常量,后面对式子做变换时直接将其看成常数1即可
\]
仅仅通过这个式子,我们还看不出来这个函数有什么性质,所以我们对该函数进行化简。
E(\Delta x, \Delta y)
&= \sum_{(x,y)\subset W}w(x,y)[I(x+\Delta x,y+\Delta y)-I(x,y)]^2
\\\\
&·= \sum_{(x,y)\subset W}w(x,y)[I(x,y)+\Delta x I_x+\Delta y I_y - I(x,y)]^2 \ \ \ \ \ (*)
\\\\
&= \sum_{(x,y)\subset W}w(x,y)(\Delta x^2 I_x^2 + \Delta y^2 I_y^2 + 2\Delta x \Delta y I_x I_y)
\\\\
&= \sum_{(x,y)\subset W}w(x,y)
\begin{bmatrix}
\Delta x \
\Delta y
\end{bmatrix}
\begin{bmatrix}
I_x^2 & I_x I_y \\
I_x I_y &I_y^2
\end{bmatrix}
\begin{bmatrix}
\Delta x \\
\Delta y
\end{bmatrix} \ \ \ \ \ (**)
\\\\
&= \begin{bmatrix}
\Delta x \
\Delta y
\end{bmatrix}
\{
\sum_{(x,y)\subset W}w(x,y)
\begin{bmatrix}
I_x^2 & I_x I_y \\
I_x I_y &I_y^2
\end{bmatrix}
\}
\begin{bmatrix}
\Delta x \\
\Delta y
\end{bmatrix}
\\\\
&= \begin{bmatrix}
\Delta x \
\Delta y
\end{bmatrix}
\begin{bmatrix}
\sum_{(x,y)\subset W}w(x,y) I_x^2 & \sum_{(x,y)\subset W}w(x,y) I_x I_y \\
\sum_{(x,y)\subset W}w(x,y) I_x I_y & \sum_{(x,y)\subset W}w(x,y) I_y^2
\end{bmatrix}
\begin{bmatrix}
\Delta x \\
\Delta y
\end{bmatrix}
\\\\
&= \begin{bmatrix}
\Delta x \
\Delta y
\end{bmatrix}
\ \{R^{-1}
\begin{bmatrix}
\lambda_1 & 0 \\
0 & \lambda_2
\end{bmatrix}
\ R\}
\begin{bmatrix}
\Delta x \\
\Delta y
\end{bmatrix} \ \ \ \ \ (其中,R是大小为2\times 2的矩阵)\ \ \ (***)
\end{aligned}
\]
对于上面的过程,大家大概率看不太懂。别急,且听我一一道来
在 \((*)\) 式中,我们将\(\Delta x和\)\(\Delta y\)看做极小量,对\(I(x+\Delta x,y+\Delta y)\)使用了二元函数的全微分公式进行展开(其实就是对二元函数进行泰勒展开并省去高阶小量)(如果不是很明白全微分是什么东西,可以参考我的一篇博客:https://www.cnblogs.com/Asaka-QianXiang/p/17172567.html,自认为是讲的比较直观清楚的了)而对\(I(x+\Delta x,y+\Delta y)\)进行如此操作的意图则是将变量\(\Delta x,\Delta y\)给分离出来,这样就能够方便我们对函数\(E(\Delta x, \Delta y)\)进行分析,其实\((**)\)式的意图也是如此。
在\((**)\)式中,式子\((\Delta x^2 I_x^2 + \Delta y^2 I_y^2 + 2\Delta x \Delta y I_x I_y)\)是以\(\Delta x,\Delta y\)为变量的二次型,于是我们写出该二次型的矩阵,也就是\((**)\)式的东西。而我们这么处理的原因,就是对变量进行分离(用术语来说就是“解耦”)。我们的\(\Delta x, \Delta y\)是变量,而\(I_x,I_y\)都相当于常量,进行上述分解后,我们只需要研究矩阵:
\sum_{(x,y)\subset W}w(x,y) I_x^2 & \sum_{(x,y)\subset W}w(x,y) I_x I_y \\
\sum_{(x,y)\subset W}w(x,y) I_x I_y & \sum_{(x,y)\subset W}w(x,y) I_y^2
\end{bmatrix}
\]
为了方便书写,我们将该矩阵命名为\(M\)
在矩阵\(M\)里,\(I_x,I_y\)都是图像\(I\)在\((x,y)\)处沿\(x,y\)轴的梯度(也就是\(I\)对\(x,y\)的偏导数,通俗点说就是斜率)。我们再仔细观察矩阵\(M\),可以发现它是一个实对称矩阵。我们对实对称矩阵\(M\)进行对角化处理(其实也是“解耦”,再具体点说,是将矩阵\(M\)变换到正交坐标系里),也就是\((***)\)式。对矩阵\(M\)对角化处理后,我们得到\(M\)的两个特征值\(\lambda_1\)和\(\lambda_2\),这两个特征值的分别反映了图像在\(x,y\)方向的梯度信息。
是不是有点晕了,不要紧,我们能看懂最后的\((***)\)式即可。当然,不要忘了我们最根本的目的:找到窗口像素变化很大的地方,也就是\(\Delta x,\Delta y\)稍微变一变,我的\(E(\Delta x,\Delta y)\)就能产生剧烈变化。那么我们怎么将这个剧烈变化体现在我们的式子上呢?显然,就是我们的\(\Delta x, \Delta y\)的系数很大。在最终化简出来的\((***)\)式中,\(\Delta x, \Delta y\)的系数是\(R^{-1}
\begin{bmatrix}
\lambda_1 & 0 \\
0 & \lambda_2
\end{bmatrix}
\ R\)(令其为\(A\)),也就是除了\(\Delta x, \Delta y\)以外且与其内积的部分。学过线性代数中实对称矩阵对角化知识的同学都知道,我们的矩阵\(R\)里面包含的都是一堆正交的单位向量,也就是说,我们想知道系数\(A\)的大小,看矩阵\(R\)是没有用的,因为单位向量的模长是1,所以我们只能去分析矩阵\(\begin{bmatrix}
\lambda_1 & 0 \\
0 & \lambda_2
\end{bmatrix}\)。
我们刚刚也说了,\(\lambda_1\)和\(\lambda_2\)分别反映了图像在\(x,y\)方向的梯度信息。怎么去理解呢?我们可以考虑一个特殊情况:图像只包含垂直\(x\)轴或垂直\(y\)轴的边缘信息,也就是说,我们的\(I_x\),\(I_y\)中有一个为0(不妨令\(I_y\)为0),因此矩阵M里的元素就只剩\(\sum_{(x,y)\subset W}w(x,y) I_x^2\)了,其实也就是\(\lambda_1\)。即\(\lambda_1\)特征值反映了x轴方向的梯度信息,后同理即可。
这样,我们的\(E(\Delta x, \Delta y)\)函数就分析的差不多了,那么我们该怎么去综合这些信息,进而在图像上进行评估并找出角点呢?
我们还是要回到\(\lambda_1\)和\(\lambda_2\)。我们知道它们反映了图像梯度信息,那么当图像分别包含平坦、边缘、角点信息时,我们的\(\lambda_1\)和\(\lambda_2\)是怎样的呢?
首先,考虑平坦信息。此时,不管从哪个方向看,梯度几乎都是0,因为非常平坦,所以此时,\(\lambda_1,\lambda_2\)都很小
其次,考虑边缘信息。由于我们的\((***)\)式其实已经将图像所有方向的梯度信息给变换到了\(\lambda_1,\lambda_2\)组成的矩阵上,也就是一组沿\(x,y\)坐标轴的正交向量(有点难理解),所以我们只要考虑水平边缘或垂直边缘即可。水平边缘时,沿x的梯度变化很大,即\(\lambda_2\)很小(接近0),\(\lambda_1\)很大;垂直边缘时,同理,\(\lambda_1\)很小(接近0),\(\lambda_2\)很大。
最后,考虑角点信息。此时不管沿哪个方向,梯度变化都很大,所以我们的\(\lambda_1,\lambda_2\)都很大。
有了上面的规律还不够,我们还要将其反映到一个函数上,也就是需要构造一个函数\(R(\lambda_1,\lambda_2)\),当\(\lambda_1,\lambda_2\)处于不同状态(角点和非角点)时,函数\(R\)会有显著不同的取值。(注意,这里的\(R\)函数与上文的\(R\)矩阵只是符号相同,意义完全不同)
我们有以下函数:
\]
其中,R称为角点相应函数,det表示矩阵的行列式(也就是特征值之积),trace表示矩阵的迹(也就是对角线元素之和,即特征值之和),k为一个经验常数,在范围\((0.04, 0.06)\)之间
用\(\lambda_1,\lambda_2\)表示函数R,即:
\]
很容易验证:
当\(\lambda_1,\lambda_2\)都很小(接近0),也就是平坦信息时,R也很小
当\(\lambda_1,\lambda_2\)有一个很大,另一个很小时(边缘信息),R会有一个不是很小的数(设为\(t\))
当\(\lambda_1,\lambda_2\)都很大且近似相等时(角点信息),R会有一个比\(t\)大的多的值
也就是说,我们只要设置一个阈值(threshold),然后将滑动窗口遍历整个图像,找到\(R(\lambda_1, \lambda_2)>threshold\)的位置\((x,y)\),那么这个\((x,y)\)就是我们需要找的角点位置。
梳理一下,Harris角点检测的整个流程就是:
- 输入灰度图像\(Img\)
- 使用\(Sobel\)算子等梯度算子计算图像上每一个点\((x,y)\)处的梯度\(I_x, I_y\)
- 对图像里的每一个像素点,构造矩阵\(M\),里面需要的参数有:窗口函数\(w(x,y)\),窗口里所有像素点的梯度信息\(I_x,I_y\)
- 求解角点响应函数\(R\),筛选函数值大于threshold的点作为角点
- 统计所有角点信息并输出
当然,OpenCV里有API函数供我们直接调用,因此我们只需要调用现有函数即可。
函数原型:
cv2.cornerHarris(src, dst, blockSize, ksize, k, borderType=BORDER_DEFAULT)
参数:
src: 输入的灰度图像
dst: Harris检测器输出的矩阵,大小等于输入图像,矩阵里每一个位置的值为角点响应函数R的值
blockSize: 角点检测中指定区域的大小(也就是W窗口的长或宽)
ksize: Sobel求梯度操作中Sobel算子的大小
k: 角点响应函数中的经验常数k
代码例程(python):
import cv2
import numpy as np
# 图像预处理
img = cv2.imread('Example4.png', flags=cv2.IMREAD_GRAYSCALE)
img_bgr = cv2.imread('Example.jpg')
img = cv2.resize(img, None, fx=0.5, fy=0.5)
img_bgr = cv2.resize(img_bgr, None, fx=0.5, fy=0.5)
kernel = np.ones((3,3), dtype=np.uint8)
img_open = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel, iterations=3)
img_close = cv2.morphologyEx(img_open, cv2.MORPH_CLOSE, kernel, iterations=10)
img_erode = cv2.morphologyEx(img_close, cv2.MORPH_ERODE, kernel, iterations=4)
img_blur = cv2.medianBlur(img_erode, 39)
cv2.imshow('blur', img_blur)
# Harris角点检测
harris_detector = cv2.cornerHarris(img_blur, 2, 3, 0.02) # Harris角点检测器
dst = cv2.dilate(harris_detector, kernel, iterations=3) # 为了使检测出来的角点更容易看见,所以膨胀
thresh = 0.011 * dst.max() # 设置阈值
img_bgr[dst > thresh] = [0, 0, 255] # 将角点标记为红色
cv2.imshow('dst', img_bgr)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果:
可以发现,下方我们检测出来了很多角点,而且距离都很近,这时候我们就可以考虑使用非极大值抑制(Non-Maximum Suppression)算法。
算法内容可以自己上网查阅相关资料,这里简述下我的代码流程:
- 建立一个列表pt,存储检测到的角点坐标、Harris检测器响应函数大小信息
- 对列表pt按照响应大小进行降序排列
- 定义一个函数,能够返回图像上两个点距离的平方
- 设置阈值,如果两点距离平方大于这个阈值,就将响应较小的点丢弃,筛选出响应较大的点进入列表selected_pt
完整代码如下:
import cv2
import numpy as np
# 图像预处理
img = cv2.imread('Example4.png', flags=cv2.IMREAD_GRAYSCALE)
img_bgr = cv2.imread('Example.jpg')
img = cv2.resize(img, None, fx=0.5, fy=0.5)
img_bgr = cv2.resize(img_bgr, None, fx=0.5, fy=0.5)
kernel = np.ones((3,3), dtype=np.uint8)
img_open = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel, iterations=3)
img_close = cv2.morphologyEx(img_open, cv2.MORPH_CLOSE, kernel, iterations=10)
img_erode = cv2.morphologyEx(img_close, cv2.MORPH_ERODE, kernel, iterations=4)
img_blur = cv2.medianBlur(img_erode, 39)
cv2.imshow('blur', img_blur)
# Harris角点检测
harris_detector = cv2.cornerHarris(img_blur, 2, 3, 0.02) # Harris角点检测器
thresh = 0.011 * harris_detector.max() # 设置阈值
# NMS非极大值抑制部分
pt = []
for h in range(harris_detector.shape[0]):
for w in range(harris_detector.shape[1]):
if harris_detector[h, w] > thresh:
pt.append([h, w, harris_detector[h,w]])
# 对pt中的元素按元素的第三个值降序排列
pt = sorted(pt, key=lambda x:x[2], reverse=True)
# 获取两点欧式距离的平方的函数
def getDist(pt1, pt2):
return (pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])
selected_pt = []
t = 500 # 阈值,两点距离小于该值的话就取角点响应最强的点
selected_pt.append(pt[0])
for p in pt[1:]:
if getDist(p, pt[0]) >= t:
selected_pt.append(p)
print(selected_pt)
# 绘图,显示角点
for p in selected_pt:
cv2.circle(img_bgr, (p[1], p[0]), 3, (0,0,255), 2)
cv2.imshow('dst', img_bgr)
cv2.waitKey(0)
cv2.destroyAllWindows()
![image]()
输出的角点位置略有偏差,大家可以通过修改代码自行优化
Shi-Tomasi角点检测
相比较于Harris角点检测,Shi-Tomasi检测器修改了Harris检测器中的角点响应函数R。
Shi-Tomasi 发现角点的稳定性其实和矩阵 M 的较小特征值有关,于是直接用较小的那个特征值作为分数。这样就不用调整k值了。如果矩阵M的两个特征值中较小的那一个大于设定的阈值,那么这个点是角点;如果两个特征值都小于阈值,那较小的特征值也必定小于阈值,那这个点就是平坦区域的点;如果其中一个特征值大于阈值而另外一个特征值小于阈值,那么这个点就是边缘点。所以我们只需要判断矩阵M的两个特征值中较小的那一个特征值是否是大于阈值,如果大于阈值这个点就是强角点。
即角点响应函数修改为:
\]
OpenCV中,我们使用cv2.goodFeaturesToTrack()函数进行Shi-Tomasi角点检测。
函数原型如下:
corners = cv2.goodFeaturesToTrack(img,
maxCorners,
qualityLevel,
minDistance,
mask,
blockSize,
gradientSize,
useHarrisDetector=False,
k=0.04
)
参数:
corners: 输出的角点向量
img: 输入图像
maxCorners: 能输出的最大角点个数(若maxCorners<=0,则默认无最大值限制)
qualityLevel: 参数用于描述图像角点的最小可接受质量。该参数值会乘以最佳角点质量度量,即最小特征值(参见cornerMinEigenVal)或Harris函数响应(参见cornerHarris)。质量度量小于该乘积的角点将被拒绝。例如,如果最佳角点的质量度量为1500,而qualityLevel=0.01,则所有质量度量小于15的角点将被拒绝。
minDistance: 输出角点的两点最小距离,相当于NMS算法筛选角点
mask: 基本不用管
blockSize: 同cornerHarris中的参数
应用实例:
import cv2
import numpy as np
# Shi-Tomasi角点检测部分
# 图像预处理
img = cv2.imread('Example4.png', flags=cv2.IMREAD_GRAYSCALE)
img_bgr = cv2.imread('Example.jpg')
img = cv2.resize(img, None, fx=0.5, fy=0.5)
img_bgr = cv2.resize(img_bgr, None, fx=0.5, fy=0.5)
kernel = np.ones((3,3), dtype=np.uint8)
img_open = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel, iterations=3)
img_close = cv2.morphologyEx(img_open, cv2.MORPH_CLOSE, kernel, iterations=10)
img_erode = cv2.morphologyEx(img_close, cv2.MORPH_ERODE, kernel, iterations=4)
img_blur = cv2.medianBlur(img_erode, 39)
cv2.imshow('blur', img_blur)
# shi-tomasi角点检测
corners = cv2.goodFeaturesToTrack(img_blur, 4, 0.01, 100, blockSize=10)
print(corners)
# 显示角点
for i in corners:
x,y = i.ravel()
cv2.circle(img_bgr,(int(x),int(y)),5,(0,0,255),-1)
cv2.imshow('dst', img_bgr)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出图像:
## 使用说明
一般来说,我们使用cv2.goodFeaturesToTrack函数来进行角点检测(即Shi-Tomasi检测器)。在OpenCV官方教程中,该函数说明如下:
我也仅仅说明一下需要注意的几个参数:
- 返回值corners是一个numpy数组。比如说,在上文的例子中,我们将corners 给打印出来:
想要处理这个corners数组,我们可以对里面的元素进行遍历,比如:
for i in corners:
...
但请注意,我们的i此时还是一个形如 [[811. 275.]] 的嵌套数组。想要将里面的数字提取出来,我们可以使用python内置的ravel方法:
for i in corners:
x,y = i.ravel()
这样,我们就将检测器提取出的角点坐标转移到x,y变量中了,后续处理也方便不少。
- qualityLevel:调节该参数能够让我们控制角点数量:
我们的角点响应函数\(R=min(\lambda_1,\lambda_2)\),而且我们输入图像的每一个像素点都对应着一个响应函数R的值。假如说,我们的图像有一个像素点,其响应函数R的值最大(设为\(m\)),qualityLevel设为\(0.01\),那么在整个图像中,只要一个像素点对应函数R的值大于\(0.01*m\),那么这个点就会被认为是角点。
- blockSize:就是我们在角点检测原理一节中提及的滑动窗口W的长或宽。
调参Trick
一般来说,按以下要点调参即可:
- 想要多少角点,就将maxCorner设多大(建议一开始设大一些,然后逐渐缩小)
- qualityLevel一般0.01左右,qualityLevel的作用相当于阈值
- 检测出的角点密集就试试增大minDistance
- 需要提取的角点比较平滑(接近弧状),blockSize就给大些(如10),角点比较尖锐,blockSize就给小点(如3)
Harris/Shi-Tomasi角点检测的更多相关文章
- OpenCV——Harris、Shi Tomas、自定义、亚像素角点检测
#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace st ...
- OpenCV计算机视觉学习(13)——图像特征点检测(Harris角点检测,sift算法)
如果需要处理的原图及代码,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice 前言 ...
- OpenCV角点检测源代码分析(Harris和ShiTomasi角点)
OpenCV中常用的角点检测为Harris角点和ShiTomasi角点. 以OpenCV源代码文件 .\opencv\sources\samples\cpp\tutorial_code\Trackin ...
- OpenCV角点检测goodFeaturesToTrack()源代码分析
上面一篇博客分析了HARRIS和ShiTomasi角点检测的源代码.而为了提取更准确的角点,OpenCV中提供了goodFeaturesToTrack()这个API函数,来获取更加准确的角点位置.这篇 ...
- Opencv学习笔记------Harris角点检测
image算法测试iteratoralgorithmfeatures 原创文章,转载请注明出处:http://blog.csdn.net/crzy_sparrow/article/details/73 ...
- Harris角点及Shi-Tomasi角点检测(转)
一.角点定义 有定义角点的几段话: 1.角点检测(Corner Detection)是计算机视觉系统中用来获得图像特征的一种方法,广泛应用于运动检测.图像匹配.视频跟踪.三维建模和目标识别等领域中.也 ...
- 【OpenCV】角点检测:Harris角点及Shi-Tomasi角点检测
角点 特征检测与匹配是Computer Vision 应用总重要的一部分,这需要寻找图像之间的特征建立对应关系.点,也就是图像中的特殊位置,是很常用的一类特征,点的局部特征也可以叫做“关键特征点”(k ...
- 角点检测:Harris角点及Shi-Tomasi角点检测
角点 特征检测与匹配是Computer Vision 应用总重要的一部分,这需要寻找图像之间的特征建立对应关系.点,也就是图像中的特殊位置,是很常用的一类特征,点的局部特征也可以叫做“关键特征点”(k ...
- OpenCV教程(45) harris角的检测(3)
在前面一篇教程中,我们通过取局部最大值的方法来处理检测结果,但是从图像中可以看到harris角的分布并不均匀,在纹理颜色比较深的地方检测的harris角结果更密集一些.本章中,我们使用一个 ...
- harris角点检测的学习
Harris通过运用微分运算和自相关矩阵改进了Moravec角点检测算法.用微分算子重新定义灰度强度变化的公式,其灰度强度变化表示为: 式中的wu,v为高斯窗口在(u,v)处的系数.X,Y它们是像素点 ...
随机推荐
- ElasticSearch7.3学习(三)----采用restful风格 批量(bulk)增删改
Bulk 操作是将文档的增删改查一些列操作,通过一次请求全都做完.目的是减少网络传输次数. 语法: POST /_bulk {"action": {"metadata&q ...
- [JVM] Java内存分配
Java内存分配 程序计数器 程序计数器是一块较小的内存区域,作用可以看做是当前线程执行的字节码的位置指示器.分支.循环.跳转.异常处理和线程恢复等基础功能都需要依赖这个计算器来完成. 虚拟机栈 虚拟 ...
- Argocd学习
argocd官网文档链接 ArgoCD官网文档 在K8S集群使用argocd命令将集群添加到argcd的cluster列表中 argocd cluster add kubernetes-admin@i ...
- JS leetcode 两数之和 II - 输入有序数组 题解分析
壹 ❀ 引 我在JS leetcode 两数之和 解答思路分析一文中首次解决两数之和等于目标值的问题,那么今天遇到的是两数之和的升级版,题目为leetcode167. 两数之和 II - 输入有序数组 ...
- C# 12 中新增的八大功能你都知道吗?
前言 转眼之间C#都已经更新到了12了,那么C# 12 中新增的八大功能你都了解过吗?今天我们来简单介绍一下C# 12 中新增的八大功能. C#/.NET该如何自学入门?:https://www.cn ...
- [WPF] MediaElement播放HDR视频泛黄、颜色显示不正确应该如何解决?
当我们在使用MediaElement控件播放HDR视频时会遇到颜色发灰.泛黄的情况,难道是因为控件做的有问题? 其实并不是程序问题,只是我们普通的应用程序工作在8bit色深的环境中,而HDR色深为10 ...
- Javascript中的var变量声明作用域问题
先看一下这两段代码的执行结果 var name2 = 'What!'; function a() { if (typeof name2 === 'undefined') { console.log(' ...
- Spring Boot学生信息管理系统项目实战-1.项目介绍
1.获取源码 需要源码的朋友,请捐赠任意金额后留下邮箱发送:) 2.项目背景 刚开始自己帮教师朋友写的,核心是学生信息的导入导出功能.后来又扩展了几个模块. 水平一般能力有限,觉着有用的朋友可以拿去自 ...
- Qt开发技术:QtCharts(一)QtCharts基本介绍以及图表框架详解
若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...
- java字节、位移以及进制转换
数据存储方式 众所周知,java中的数据都是以二进制的形式存储在计算机中的,但是我们看到的数据怎么是10进制的,因为java提供了很多进制自动转换的方式. 位移 向左位移是*2的幂次,一般都是正数操作 ...