<图像处理> Harris角点检测

Harris角点检测原理

Harris角点检测是一种计算机视觉中常用的角点检测算法,用于在图像中检测出角点特征。角点通常被定义为两条边的交点,或者说,角点的局部邻域应该具有两个不同区域的不同方向的边界。Harris角点检测算法是最常用且最基础的角点检测算法之一。

Harris角点检测算法的原理是通过计算图像中每个像素点的响应函数来判断其是否为角点。该响应函数基于图像的灰度变化和局部窗口的协方差矩阵,通过计算特征值来确定角点的位置。当特征值较大时,表示该像素点处存在角点。

优点: 对图像旋转、尺度变化和亮度变化具有一定的不变性,且计算简单快速。

当一幅图像 f f f,检测窗口在图像上滑动,当检测窗口在点 ( x , y ) (x,y) (x,y)处位移 ( Δ x , Δ y ) (\Delta x,\Delta y) (Δx,Δy)后,它的自相关函数为:
C ( x , y ) = ∑ x i ∈ w ∑ y i ∈ w ( f ( x i , y i ) − f ( x i − Δ x , y i − Δ y ) ) 2 C(x,y)=\sum_{x_i\in w} \sum_{y_i \in w}(f(x_i,y_i)-f(x_i-\Delta x,y_i-\Delta y))^2 C(x,y)=xiwyiw(f(xi,yi)f(xiΔx,yiΔy))2
角点不会受光圈问题的影响,对于所有 Δ x , Δ y , S w ( Δ x , Δ y ) \Delta x,\Delta y,S_w(\Delta x,\Delta y) Δx,ΔySw(Δx,Δy)都是高响应,如果平移图像用一阶泰勒展开近似,则可表示为
f ( x i − Δ x , y i − Δ y ) ≈ ( f ( x i , y i ) + [ ∂ f ( x i , y i ) ∂ x ∂ f ( x i , y i ) ∂ y ] [ Δ x Δ y ] f(x_i-\Delta x,y_i-\Delta y)\approx (f(x_i,y_i) +\begin{bmatrix}\frac{\partial f(x_i,y_i)}{\partial x}&\frac{\partial f(x_i,y_i)}{\partial y}\end{bmatrix} \begin{bmatrix} \Delta x \\ \Delta y \end{bmatrix} f(xiΔx,yiΔy)(f(xi,yi)+[xf(xi,yi)yf(xi,yi)][ΔxΔy]
带入:
C ( x , y ) = ∑ x i ∈ w ∑ y i ∈ w ( f ( x i , y i ) − f ( x i , y i ) − [ ∂ f ( x i , y i ) ∂ x ∂ f ( x i , y i ) ∂ y ] [ Δ x Δ y ] ) 2 C(x,y)=\sum_{x_i\in w} \sum_{y_i \in w}\Bigg(f(x_i,y_i)-f(x_i,y_i)-\begin{bmatrix}\frac{\partial f(x_i,y_i)}{\partial x}&\frac{\partial f(x_i,y_i)}{\partial y}\end{bmatrix} \begin{bmatrix} \Delta x \\ \Delta y \end{bmatrix}\Bigg)^2 C(x,y)=xiwyiw(f(xi,yi)f(xi,yi)[xf(xi,yi)yf(xi,yi)][ΔxΔy])2
= ∑ x i ∈ w ∑ y i ∈ w ( − [ ∂ f ( x i , y i ) ∂ x ∂ f ( x i , y i ) ∂ y ] [ Δ x Δ y ] ) 2 =\sum_{x_i\in w} \sum_{y_i \in w}\Bigg(-\begin{bmatrix}\frac{\partial f(x_i,y_i)}{\partial x}&\frac{\partial f(x_i,y_i)}{\partial y}\end{bmatrix} \begin{bmatrix} \Delta x \\ \Delta y \end{bmatrix}\Bigg)^2 =xiwyiw([xf(xi,yi)yf(xi,yi)][ΔxΔy])2
= ∑ x i ∈ w ∑ y i ∈ w ( [ ∂ f ( x i , y i ) ∂ x ∂ f ( x i , y i ) ∂ y ] [ Δ x Δ y ] ) 2 =\sum_{x_i\in w} \sum_{y_i \in w}\Bigg(\begin{bmatrix}\frac{\partial f(x_i,y_i)}{\partial x}&\frac{\partial f(x_i,y_i)}{\partial y}\end{bmatrix} \begin{bmatrix} \Delta x \\ \Delta y \end{bmatrix}\Bigg)^2 =xiwyiw([xf(xi,yi)yf(xi,yi)][ΔxΔy])2
因为 u 2 = u T u u^2=u^Tu u2=uTu
= ∑ x i ∈ w ∑ y i ∈ w [ Δ x , Δ y ] ( [ ∂ f ∂ x ∂ f ∂ y ] [ ∂ f ∂ x ∂ f ∂ y ] ) [ Δ x Δ y ] =\sum_{x_i\in w} \sum_{y_i \in w}[\Delta x,\Delta y]\Bigg(\begin{bmatrix} \frac {\partial f}{\partial x} \\ \frac {\partial f}{\partial y} \end{bmatrix}\begin{bmatrix} \frac {\partial f}{\partial x} \frac {\partial f}{\partial y} \end{bmatrix}\Bigg) \begin{bmatrix} \Delta x \\ \Delta y \end{bmatrix} =xiwyiw[Δx,Δy]([xfyf][xfyf])[ΔxΔy]
= [ Δ x , Δ y ] ( ∑ x i ∈ w ∑ y i ∈ w [ ∂ f ∂ x ∂ f ∂ y ] [ ∂ f ∂ x ∂ f ∂ y ] ) [ Δ x Δ y ] =[\Delta x,\Delta y]\Bigg(\sum_{x_i\in w} \sum_{y_i \in w}\begin{bmatrix} \frac {\partial f}{\partial x} \\ \frac {\partial f}{\partial y} \end{bmatrix}\begin{bmatrix} \frac {\partial f}{\partial x} \frac {\partial f}{\partial y} \end{bmatrix}\Bigg) \begin{bmatrix} \Delta x \\ \Delta y \end{bmatrix} =[Δx,Δy](xiwyiw[xfyf][xfyf])[ΔxΔy]
= [ Δ x , Δ y ] A w ( x , y ) [ Δ x Δ y ] =[\Delta x,\Delta y]A_w(x,y)\begin{bmatrix} \Delta x \\ \Delta y \end{bmatrix} =[Δx,Δy]Aw(x,y)[ΔxΔy]
A w ( x , y ) A_w(x,y) Aw(x,y)表示图像区域 w w w在点 ( x , y ) = ( 0 , 0 ) (x,y)=(0,0) (x,y)=(0,0)处的二阶导数的一半。 A A A为:
A ( x , y ) = 2 ∙ [ ∑ x i ∈ w ∑ y i ∈ w ( ∂ f ( x i , y i ) ∂ x ) 2 ∑ x i ∈ w ∑ y i ∈ w ∂ f ( x i , y i ) ∂ x ∂ f ( x i , y i ) ∂ y ∑ x i ∈ w ∑ y i ∈ w ∂ f ( x i , y i ) ∂ x ∂ f ( x i , y i ) ∂ y ∑ x i ∈ w ∑ y i ∈ w ( ∂ f ( x i , y i ) ∂ y ) 2 ] A(x,y)=2\bullet \begin{bmatrix} \sum_{x_i\in w} \sum_{y_i \in w}(\frac{\partial f(x_i,y_i)}{\partial x})^2 & \sum_{x_i\in w} \sum_{y_i \in w} \frac{\partial f(x_i,y_i)}{\partial x}\frac{\partial f(x_i,y_i)}{\partial y} \\ \sum_{x_i\in w} \sum_{y_i \in w} \frac{\partial f(x_i,y_i)}{\partial x}\frac{\partial f(x_i,y_i)}{\partial y}& \sum_{x_i\in w} \sum_{y_i \in w}(\frac{\partial f(x_i,y_i)}{\partial y})^2 \end{bmatrix} A(x,y)=2[xiwyiw(xf(xi,yi))2xiwyiwxf(xi,yi)yf(xi,yi)xiwyiwxf(xi,yi)yf(xi,yi)xiwyiw(yf(xi,yi))2]

局部结构矩阵 A A A代表邻域的变化,其有两个特征值 λ 1 , λ 2 \lambda_1,\lambda_2 λ1λ2,有以下三种情形出现:
1、两个特征值都很小,这意味着,图像f在检测点处平坦——在该点没有边缘或角点;
2、一个特征值很小,另一个很大,局部邻域呈脊状,如有垂直于脊部的微小移动,图像f将发生显著的变化;
3、两个特征值都很大,任何方向的移动,都会造成图像f的显著变化,此时,检测到一个角点。

对于求解特征值需要一元二次方程,这必然会降低运算效率。因此,通过计算响应函数 R ( A ) = d e t ( A ) − k ( t r a c e ( A ) ) 2 R(A)=det(A)-k(trace(A))^2 R(A)=det(A)k(trace(A))2,即 R = λ 1 λ 2 − k ( λ 1 + λ 2 ) 2 R=\lambda_1\lambda_2-k(\lambda_1+\lambda_2)^2 R=λ1λ2k(λ1+λ2)2
A ( x , y ) = [ a b b c ] A(x,y)=\begin{bmatrix} a&b\\b&c \end{bmatrix} A(x,y)=[abbc]
d e t ( A ) = λ 1 λ 2 = a c − b 2 det(A)=\lambda_1\lambda_2=ac-b^2 det(A)=λ1λ2=acb2
t r a c e ( A ) = λ 1 + λ 2 = a + c trace(A)=\lambda_1+\lambda_2=a+c trace(A)=λ1+λ2=a+c
所以,
R = a c − b 2 − k ( a + c ) 2 R=ac-b^2-k(a+c)^2 R=acb2k(a+c)2

检测步骤:

  1. 计算图像 I I I的梯度,获取水平和垂直方向上的梯度 I x I_x Ix I y I_y Iy
  2. 构建协方差矩阵,计算每个像素点的协方差矩阵,即计算两个方向梯度的乘积 I x 2 = I x ∗ I x , I y 2 = I y ∗ I y , I x y = I x ∗ I y I_x^2=I_x*I_x,I_y^2=I_y*I_y,I_{xy}=I_x*I_y Ix2=IxIxIy2=IyIyIxy=IxIy
  3. 使用加权窗口 w w w(可以是高斯加权,可以是平均加权,OpenCV中用的平均加权),对 I x 2 , I y 2 , I x y I_x^2,I_y^2,I_{xy} Ix2,Iy2,Ixy进行加权处理,得到自相关元素 a = I x 2 ∗ w , b = I x y 2 ∗ w , c = I y 2 ∗ w a=I_x^2 *w,b=I_{xy}^2 *w,c=I_y^2 *w a=Ix2wb=Ixy2wc=Iy2w
  4. 计算协方差矩阵的特征值 R R R,判断每个像素点是否为角点;
  5. 根据设定的阈值,筛选出最终的角点。

OpenCV函数

void cv::cornerHarris(InputArray src, OutputArray dst, int blockSize, int ksize, double k, int borderType=BORDER_DEFAULT)Parameters
src				输入图像,单通道8位或浮点型;
dst				输出Harris算子响应图像,该变量存储的R值,类型为单通道32位浮点型;
blockSize		步骤3中的加权窗口尺寸;
ksize			Sobel算子求梯度的尺寸;
k				响应函数中的k;
borderType		填充边缘方法,见边缘填充博客;

OpenCV源码分析

cornerHarris函数在sources\modules\imgproc\src\corner.cpp文件内定义

void cv::cornerHarris( InputArray _src, OutputArray _dst, int blockSize, int ksize, double k, int borderType )
{CV_INSTRUMENT_REGION();CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(),ocl_cornerMinEigenValVecs(_src, _dst, blockSize, ksize, k, borderType, HARRIS))Mat src = _src.getMat();_dst.create( src.size(), CV_32FC1 );Mat dst = _dst.getMat();#ifdef HAVE_IPPint borderTypeNI = borderType & ~BORDER_ISOLATED;bool isolated = (borderType & BORDER_ISOLATED) != 0;
#endifCV_IPP_RUN(((ksize == 3 || ksize == 5) && (_src.type() == CV_8UC1 || _src.type() == CV_32FC1) &&(borderTypeNI == BORDER_CONSTANT || borderTypeNI == BORDER_REPLICATE) && CV_MAT_CN(_src.type()) == 1 &&(!_src.isSubmatrix() || isolated)) && IPP_VERSION_X100 >= 810, ipp_cornerHarris( src, dst, blockSize, ksize, k, borderType ));//调用计算角点特征值的函数,其中参数HARRIS表示该函数用于HARRIS算子cornerEigenValsVecs( src, dst, blockSize, ksize, HARRIS, k, borderType );
}

cornerEigenValsVecs函数:

static void
cornerEigenValsVecs( const Mat& src, Mat& eigenv, int block_size,int aperture_size, int op_type, double k=0.,int borderType=BORDER_DEFAULT )
{
#if CV_TRY_AVXbool haveAvx = CV_CPU_HAS_SUPPORT_AVX;
#endifint depth = src.depth();//计算尺度,该值在梯度计算的时候使用,主要目的是通过减小尺度,来提高平滑处理的速度double scale = (double)(1 << ((aperture_size > 0 ? aperture_size : 3) - 1)) * block_size;if( aperture_size < 0 )scale *= 2.0;if( depth == CV_8U )scale *= 255.0;scale = 1.0/scale;CV_Assert( src.type() == CV_8UC1 || src.type() == CV_32FC1 );//Dx:水平方向梯度;Dy:垂直方向梯度Mat Dx, Dy;//选择求梯度方式if( aperture_size > 0 ){Sobel( src, Dx, CV_32F, 1, 0, aperture_size, scale, 0, borderType );Sobel( src, Dy, CV_32F, 0, 1, aperture_size, scale, 0, borderType );}else{Scharr( src, Dx, CV_32F, 1, 0, scale, 0, borderType );Scharr( src, Dy, CV_32F, 0, 1, scale, 0, borderType );}Size size = src.size();Mat cov( size, CV_32FC3 );int i, j;//遍历每个像素,计算Ix^2,Iy^2,Ixyfor( i = 0; i < size.height; i++ ){float* cov_data = cov.ptr<float>(i);const float* dxdata = Dx.ptr<float>(i);const float* dydata = Dy.ptr<float>(i);#if CV_TRY_AVXif( haveAvx )j = cornerEigenValsVecsLine_AVX(dxdata, dydata, cov_data, size.width);else
#endif // CV_TRY_AVXj = 0;//指令集加速
#if CV_SIMD128{for( ; j <= size.width - v_float32x4::nlanes; j += v_float32x4::nlanes ){v_float32x4 v_dx = v_load(dxdata + j);v_float32x4 v_dy = v_load(dydata + j);v_float32x4 v_dst0, v_dst1, v_dst2;v_dst0 = v_dx * v_dx;v_dst1 = v_dx * v_dy;v_dst2 = v_dy * v_dy;v_store_interleave(cov_data + j * 3, v_dst0, v_dst1, v_dst2);}}
#endif // CV_SIMD128for( ; j < size.width; j++ ){float dx = dxdata[j];float dy = dydata[j];cov_data[j*3] = dx*dx;	//Ix^2cov_data[j*3+1] = dx*dy;	//Ixycov_data[j*3+2] = dy*dy;	//Iy^2}}//步骤3,均值滤波boxFilter(cov, cov, cov.depth(), Size(block_size, block_size),Point(-1,-1), false, borderType );//步骤4,计算特征值if( op_type == MINEIGENVAL )calcMinEigenVal( cov, eigenv );else if( op_type == HARRIS )calcHarris( cov, eigenv, k );else if( op_type == EIGENVALSVECS )calcEigenValsVecs( cov, eigenv );
}

calcHarris函数:

static void calcHarris( const Mat& _cov, Mat& _dst, double k )
{int i, j;Size size = _cov.size();
#if CV_TRY_AVXbool haveAvx = CV_CPU_HAS_SUPPORT_AVX;
#endifif( _cov.isContinuous() && _dst.isContinuous() ){size.width *= size.height;size.height = 1;}//遍历每个像素for( i = 0; i < size.height; i++ ){const float* cov = _cov.ptr<float>(i);float* dst = _dst.ptr<float>(i);#if CV_TRY_AVXif( haveAvx )j = calcHarrisLine_AVX(cov, dst, k, size.width);else
#endif // CV_TRY_AVXj = 0;//指令集加速
#if CV_SIMD128{v_float32x4 v_k = v_setall_f32((float)k);for( ; j <= size.width - v_float32x4::nlanes; j += v_float32x4::nlanes ){v_float32x4 v_a, v_b, v_c;v_load_deinterleave(cov + j * 3, v_a, v_b, v_c);v_float32x4 v_ac_bb = v_a * v_c - v_b * v_b;v_float32x4 v_ac = v_a + v_c;v_float32x4 v_dst = v_ac_bb - v_k * v_ac * v_ac;v_store(dst + j, v_dst);}}
#endif // CV_SIMD128for( ; j < size.width; j++ ){float a = cov[j*3];		//自相关元素afloat b = cov[j*3+1];	//自相关元素bfloat c = cov[j*3+2];	//自相关元素cdst[j] = (float)(a*c - b*b - k*(a + c)*(a + c));	//计算特征值R=ac-b*b-k*(a+c)*(a+c)}}
}

实战

#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"using namespace cv;
using namespace std;int main()
{const char* imageName = "SiliconChip.bmp";cv::Mat src = imread(imageName, IMREAD_GRAYSCALE);int blocksize = 3;int apertureSize = 7;double k = 0.001;int thresh = 80; //步骤5中阈值Mat dst;cv::cornerHarris(src, dst, blocksize, apertureSize, k, BORDER_DEFAULT);//归一化R图像,使其在0-255之间Mat dst_norm;cv::normalize(dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1);//将R图像转换成CV_8U类型,以便能够正确显示Mat dst_norm_scaled;cv::convertScaleAbs(dst_norm, dst_norm_scaled);Mat srcColor;cvtColor(src, srcColor, COLOR_GRAY2RGB);//转rgb//步骤5,在角点画圈for (int i = 0;i < dst.rows;i++){for (int j = 0;j < dst.cols;j++){if (dst_norm_scaled.at<uchar>(i, j) > thresh){cv::circle(srcColor, Point(j, i), 15, Scalar(0, 0, 255), 1, 8, 0);}}}system("pause");return 0;
}

检测结果显示:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用亚马逊 云服务器 EC2 和S3免费套餐搭建私人网盘

网盘是一种在线存储服务&#xff0c;提供文件存储&#xff0c;访问&#xff0c;备份&#xff0c;贡献等功能&#xff0c;是我们日常中不可或缺的一种服务。很多互联网公司都为个人和企业提供免费的网盘服务。但这些免费服务都有一些限制&#xff0c;比如限制下载速度&#xff0…

Vue3 封装 element-plus 图标选择器

一、实现效果 二、实现步骤 2.1. 全局注册 icon 组件 // main.ts import App from ./App.vue; import { createApp } from vue; import * as ElementPlusIconsVue from element-plus/icons-vueconst app createApp(App);// 全局挂载和注册 element-plus 的所有 icon app.con…

WebGL 选中物体

目录 前言 如何实现选中物体 示例程序&#xff08;PickObject.js&#xff09; 代码详解 gl.readPixels&#xff08;&#xff09;函数规范 示例效果 前言 有些三维应用程序需要允许用户能够交互地操纵三维物体&#xff0c;要这样做首先就得允许用户选中某个物体。对物体…

大模型存在“反转诅咒”现象,无法处理反向问题;Langchain课程资源

&#x1f989; AI新闻 &#x1f680; 大模型存在“反转诅咒”现象&#xff0c;无法处理反向问题 摘要&#xff1a;最新研究发现&#xff0c;大语言模型存在“反转诅咒”现象&#xff0c;即明知道“A 是 B”&#xff0c;却答不出“B 是 A”。研究人员进行了两项实验&#xff0…

php万能表单系统源码 支持自定义+收费表单活动报名 适合多行业

在众多的表单系统中&#xff0c;PHP万能表单系统因其灵活性和可扩展性备受开发者的青睐。PHP万能表单系统是一款基于PHP语言的表单生成器&#xff0c;它可以帮助开发者快速生成各种类型的表单&#xff0c;如注册、登录、留言等。下面给大家分享一款php万能表单系统源码&#xf…

【详细图文】Windows下安装RustRover和配置Rust环境

前言 Rust已经火了挺长时间了&#xff0c;连微软的Windows内核都用它来重新改写&#xff0c;可想而知其厉害之处。之前有看过Rust的教程&#xff0c;但一直没有去尝试。今天看到JetBrains出了Rust 专用的IDE&#xff1a;RustRover。作为JetBrains的粉丝&#xff0c;决定进行一…

Linux环境下使用SVN快速访问资料库?试试使用cpolar端口映射

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

Kotlin | 在for、forEach循环中正确的使用break、continue

文章目录 for循环中使用break、continueLabel标签forEach中模拟break、continue资料 Kotlin 有三种结构化跳转表达式&#xff1a; return&#xff1a;默认从最直接包围它的函数或者匿名函数返回。break&#xff1a;终止最直接包围它的循环。continue&#xff1a;继续下一次最直…

git 常用命令分享

git官网地址&#xff1a;https://git-scm.com/ 1.设置用户名 邮箱 设置用户名: git config --global user.name “name” 设置邮箱&#xff1a; git config --global user.email "email" 2.查看设置的信息 git config --list 以上设置的信息在用户目录下&#xff1…

lazada商品详情数据接口,支持多个国家站点

Lazada商品详情数据接口是一个RESTful风格的接口&#xff0c;通过HTTP协议来访问和操作资源。 Lazada商品详情API接口的使用方法如下&#xff1a; 获取Lazada平台上指定商品的详细信息&#xff0c;包括商品名称、价格、库存、分类、描述、图片等。支持通过商品ID、SKU、Selle…

有效的括号(栈的高频面试题)

一、题目描述 题目连接&#xff1a;有效的括号 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺…

Generative AI 新世界 | 扩散模型原理的代码实践之采样篇

在上一期的文章中&#xff0c;探讨了在 Amazon SageMaker Studio 上使用 QLoRA 等量化技术微调 Falcon 40B 大语言模型。而从本期开始&#xff0c;我们将一起尝试在更深的知识维度&#xff0c;继续探究生成式 AI 这一火热的新知识领域。 亚马逊云科技开发者社区为开发者们提供全…

工作薄代码之将活动工作表复制到新工作簿等

【分享成果&#xff0c;随喜正能量】得失&#xff0c;可以说是人类事业上的考验&#xff0c;不要因一时的得失影响一生的期许。得失是一时的&#xff0c;理想是一生的。。 我给VBA下的定义&#xff1a;VBA是个人小型自动化处理的有效工具。可以大大提高自己的劳动效率&#xf…

定时器之输出捕获

简介 • IC &#xff08; Input Capture &#xff09;输入捕获 • 输入捕获模式下&#xff0c;当通道输入引脚出现指定电平跳变时&#xff0c;当前 CNT 的值将被锁存到 CCR 中&#xff0c;可用于测量 PWM 波形的频率、占空比、脉冲间隔、电平持续时间等参数 • 每个高级定时器和…

多线程进阶:Callable和JUC的常见类

Callable 这是一个接口&#xff0c;类似于Runnable。 Runnable用来描述一个任务&#xff0c;描述的任务没有返回值。 Callable也是用来描述一个任务&#xff0c;描述的任务是有返回值的。 如果需要使用一个线程单独的计算出某个结果来&#xff0c;此时用Callable是比较合适…

【好玩的开源项目】Windows 12网页版的部署与使用体验

【好玩的开源项目】Windows 12网页版的部署与使用体验 一、Windows 12网页版介绍1.1 Windows 12网页版简介1.2 项目地址 二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍 三、安装httpd软件3.1 检查yum仓库3.2 安装httpd软件3.3 启动httpd服务3.4 查看httpd服务3.5 防火墙和…

springboot+canal+mysql+redis缓存双写一致性

canal官网地址&#xff1a;https://github.com/alibaba/canal/wiki/QuickStart 基本上按照官网的步骤来就行 准备 首先服务器上要安装好jdk&#xff0c;因为canal运行需要jdk,同时把canal对应的端口在服务中开放&#xff0c;否则连接不上 对于自建 MySQL , 需要先开启 Binl…

QT-day5

1、添加注册功能到数据库 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMessageBox> //消息对话框类头文件 #include <QDebug> #include <QPushButton> #include <QSqlDatabase> //数据库管理类 #include…

ChatGPT详细搭建教程+支持AI绘画

一、AI创作系统 SparkAi系统是基于很火的GPT提问进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT系统&#xff1f;小编这里写一个详细图文教程吧&#x…

SpringBoot 之配置加密

Jasypt库的使用 Jasypt是一个Java简易加密库&#xff0c;用于加密配置文件中的敏感信息&#xff0c;如数据库密码。 Jasypt库与springboot集成&#xff0c;在实际开发中非常方便。 1、引入依赖 <dependency><groupId>com.github.ulisesbocchio</groupId>&…