8.2Roberts算子边缘检测

news2024/9/20 8:06:41

基本概念

Roberts算子是一种简单的一阶导数边缘检测算子,它通过计算图像在水平和垂直方向上的梯度来检测边缘。在OpenCV中,Roberts算子可以通过手动应用卷积核来实现。Roberts算子是一组2x2的小型滤波器,用于检测图像中的垂直和水平边缘。

Roberts算子掩模Roberts算子有两个掩模,分别用于检测水平和垂直方向上的边缘:

实现步骤

1. 应用掩模:将Roberts算子的掩模应用于图像。

2. 计算梯度:计算水平方向和垂直方向上的梯度。

3. 计算梯度幅度:通过水平梯度和垂直梯度的合成来得到最终的梯度幅度。

示例代码1

下面是基于C++的OpenCV代码示例,展示了如何使用Roberts算子来检测边缘。

步骤一:包含必要的头文件
首先,确保你的项目已经正确配置了OpenCV库,并且包含了必要的头文件。

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;



步骤二:定义Roberts算子
Roberts算子的核如下所示:
Gx = [ 1  0
       0 -1 ]

Gy = [ 0  1
     -1  0 ]
在C++中,你可以通过创建一个Mat对象来表示这些核。

Mat kernelGx = (Mat_<float>(2, 2) <<
                         1, 0, 
                         0, -1);
Mat kernelGy = (Mat_<float>(2, 2) <<
                         0, 1,
                         -1, 0);

步骤三:加载并准备输入图像
你需要读取一个图像,并将其转换为灰度图像以简化边缘检测任务。

Mat src = imread("path_to_your_image.jpg", IMREAD_GRAYSCALE);
if(src.empty())
{
    std::cout << "Error: Image cannot be loaded!" << std::endl;
    return -1;
}



步骤四:应用Roberts算子
由于Roberts算子的大小是2x2,我们需要特别注意边界条件。在OpenCV中,你可以使用filter2D函数来应用自定义的卷积核。这里我们分别应用Gx和Gy核。

Mat dstGx, dstGy;

// Apply the kernels to the source image.
filter2D(src, dstGx, CV_32F, kernelGx);
filter2D(src, dstGy, CV_32F, kernelGy);

// Convert back to CV_8UC1 (unsigned char) type for display.
Mat abs_dstGx, abs_dstGy;
convertScaleAbs(dstGx, abs_dstGx);
convertScaleAbs(dstGy, abs_dstGy);

步骤五:组合结果
为了获得完整的边缘检测结果,通常需要合并Gx和Gy的结果。

Mat edges;
addWeighted(abs_dstGx, 0.5, abs_dstGy, 0.5, 0, edges);

步骤六:显示结果
最后,你可以使用imshow函数来展示原始图像和边缘检测后的结果。


namedWindow("Original Image", WINDOW_AUTOSIZE);
imshow("Original Image", src);

namedWindow("Edges", WINDOW_AUTOSIZE);
imshow("Edges", edges);

waitKey(0);

以上就是使用OpenCV和C++实现Roberts算子边缘检测的一个基本示例。
请根据实际情况调整路径和参数。

示例代码


#include "pch.h"
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;

int main()
{

	Mat kernelGx = (Mat_<float>(2, 2) <<
		1, 0,
		0, -1);
	Mat kernelGy = (Mat_<float>(2, 2) <<
		0, 1,
		-1, 0);


	Mat src = imread("012.jpeg", IMREAD_GRAYSCALE);
	if (src.empty())
	{
		std::cout << "Error: Image cannot be loaded!" << std::endl;
		return -1;
	}



	Mat dstGx, dstGy;

	// Apply the kernels to the source image.
	filter2D(src, dstGx, CV_32F, kernelGx);
	filter2D(src, dstGy, CV_32F, kernelGy);

	// Convert back to CV_8UC1 (unsigned char) type for display.
	Mat abs_dstGx, abs_dstGy;
	convertScaleAbs(dstGx, abs_dstGx);
	convertScaleAbs(dstGy, abs_dstGy);


	Mat edges;
	addWeighted(abs_dstGx, 0.5, abs_dstGy, 0.5, 0, edges);


	namedWindow("Original Image", WINDOW_NORMAL);
	imshow("Original Image", src);
	namedWindow("Edges", WINDOW_NORMAL);
	imshow("Edges", edges);
	waitKey(0);
	return 0;
}

运行结果1

filter2D

filter2D函数是OpenCV提供的一个非常强大的工具,用于在图像上应用任何类型的卷积操作。它可以用于实现多种图像处理功能,如模糊、锐化、边缘检测等。

下面详细介绍filter2D函数的用法以及其参数的意义。

函数原型

void filter2D(InputArray src, OutputArray dst, int ddepth, 
               InputArray kernel,Point anchor=Point(-1,-1), 
                double delta=0, int borderType=BORDER_DEFAULT);

参数说明
src: 输入图像。可以是多通道图像。
dst: 输出图像。它将具有与输入图像相同的尺寸和类型(除非指定不同的深度)。
ddepth: 指定输出图像的深度。如果设置为-1,则输出图像的深度与输入图像相同。否则,可以指定不同的深度,如CV_32F(32位浮点数)。
kernel: 卷积核(滤波器)。这是一个二维数组,用于定义卷积运算。核的大小通常是奇数,例如3x3或5x5,以便有一个中心点。
anchor: 卷积核相对于每个像素的锚点位置。默认情况下(Point(-1,-1)),锚点位于核的中心。你可以改变锚点的位置来控制卷积核相对于图像像素的作用方式。
delta: 可选的常数值,将在卷积操作后加到每一个像素上。
borderType: 边界处理类型。当卷积核覆盖图像的边界时,需要指定如何处理边界外的数据。OpenCV提供了多种边界处理方式,例如:
BORDER_CONSTANT: 使用常数值填充边界外区域。
BORDER_REPLICATE: 复制边界像素。
BORDER_REFLECT: 镜像反射边界。
BORDER_WRAP: 边界环绕(类似于纹理坐标)。
BORDER_REFLECT_101 或 BORDER_DEFAULT: 默认的边界反射方式。

示例代码2

下面是一个简单的例子,演示如何使用filter2D函数来应用一个3x3的Sobel核来检测水平边缘。

#include "pch.h"
#include <opencv2/opencv.hpp>
#include <iostream>

int main(int argc, char** argv)
{
	// 加载图像
	cv::Mat src = cv::imread("033.jpeg", cv::IMREAD_GRAYSCALE);
	if (src.empty())
	{
		std::cout << "Error: Image cannot be loaded!" << std::endl;
		return -1;
	}

	// 定义Sobel核
	cv::Mat kernel = (cv::Mat_<float>(3, 3) <<
		-1, -2, -1,
		0, 0, 0,
		1, 2, 1);

	// 创建输出图像
	cv::Mat dst;
	// 应用filter2D函数
	cv::filter2D(src, dst, -1, kernel);

	// 显示结果
	cv::namedWindow("Original Image", cv::WINDOW_NORMAL);
	cv::imshow("Original Image", src);

	cv::namedWindow("Filtered Image", cv::WINDOW_NORMAL);
	cv::imshow("Filtered Image", dst);

	cv::waitKey(0);
	return 0;
}

在这个例子中,我们使用了一个Sobel核来检测水平边缘。如果你想要检测垂直边缘,可以相应地改变核的值。此外,如果需要调整输出图像的深度,可以修改ddepth参数。例如,使用CV_32F可以得到浮点数输出,这有助于避免整数溢出的问题。

运行结果2

示例代码3

下面是一个使用OpenCV C++实现Roberts算子进行边缘检测的示例代码:

#include "pch.h"
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

// 定义Roberts算子掩模
static const float robertsHorizontal[] = { 1, 0, 0, -1 };// 定义Roberts交叉算子水平方向的内核
static const float robertsVertical[] = { 0, 1, -1, 0 };



void detectEdgesWithRoberts(const Mat &src, Mat &horizontal, Mat &vertical, Mat &magnitude) 
{


	// 将数据复制到一个std::vector中
	std::vector<float> data(robertsHorizontal, robertsHorizontal + 4);
	// 创建一个2x2的单通道浮点型矩阵
	Mat kernelHorizontal = Mat(2, 2, CV_32F, data.data());

	// 将数据复制到一个std::vector中
	std::vector<float> data2(robertsVertical, robertsVertical + 4);
	// 创建一个2x2的单通道浮点型矩阵
	Mat kernelVertical = Mat(2, 2, CV_32F, data2.data());


	// 水平方向的边缘检测
	filter2D(src, horizontal, CV_32F, kernelHorizontal, Point(-1, -1), 0, BORDER_REPLICATE);

	// 垂直方向的边缘检测
	filter2D(src, vertical, CV_32F, kernelVertical, Point(-1, -1), 0, BORDER_REPLICATE);



	//
	// 计算梯度幅度
	//magnitude = sqrt(horizontal.mul(horizontal) + vertical.mul(vertical));
	//一下代码来代替
	//在 OpenCV 中,cv::Mat 类没有提供 mul 成员函数用于矩阵元素级别的乘法。要执行逐元素的乘法,您应该使用 cv::multiply 函数。同时,您需要确保 magnitude 矩阵的类型适合进行平方和开方运算。
	//使用 cv::multiply 函数来计算水平和垂直梯度的平方。
	//使用 cv::sqrt 函数来计算平方和的平方根,得到梯度的幅度。
	//使用 cv::convertScaleAbs 函数将结果转换为 8 位无符号整数格式。
	//
	// 创建一个临时矩阵来保存平方和
	cv::Mat tmp;
	// 计算水平和垂直梯度的平方和
	cv::multiply(horizontal, horizontal, tmp);
	cv::multiply(vertical, vertical, tmp, 1.0);
	cv::sqrt(tmp, magnitude);
	//

	// 将结果转换为8位无符号整数
	convertScaleAbs(magnitude, magnitude);
}

int main(int argc, char** argv)
{
	/*if (argc != 2)
	{
		cout << "Usage: ./RobertsEdgeDetection <Image Path>" << endl;
		return -1;
	}*/

	// 加载图像
	Mat img = imread("27.png", IMREAD_GRAYSCALE);
	if (!img.data) 
	{
		cout << "Error opening image" << endl;
		return -1;
	}

	// 初始化输出矩阵
	Mat horizontal, vertical, magnitude;

	// 执行Roberts边缘检测
	detectEdgesWithRoberts(img, horizontal, vertical, magnitude);


	// 显示结果
	namedWindow("Original Image", WINDOW_NORMAL);
	imshow("Original Image", img);
	namedWindow("Roberts Horizontal", WINDOW_NORMAL);
	imshow("Roberts Horizontal", horizontal);
	namedWindow("Roberts Vertical", WINDOW_NORMAL);
	imshow("Roberts Vertical", vertical);
	namedWindow("Magnitude", WINDOW_NORMAL);
	imshow("Magnitude", magnitude);

	waitKey(0);
	destroyAllWindows();

	return 0;
}




代码解释
1. 定义Roberts算子掩模:使用静态数组来定义Roberts算子的两个掩模。
2. 边缘检测:使用 filter2D 函数分别计算水平方向和垂直方向上的边缘。
3. 计算梯度幅度:通过计算水平方向和垂直方向边缘的平方和的平方根来得到梯度幅度。
4. 转换数据类型:使用 convertScaleAbs 函数将浮点型数据转换为8位无符号整数,以便于显示。
5. 显示结果:使用 imshow 函数显示原始图像、水平方向边缘、垂直方向边缘以及最终的梯度幅度图。


注意事项
•噪声敏感性:Roberts算子对噪声非常敏感,因此在实际应用中,通常需要对图像进行平滑处理(如使用高斯滤波器)来减少噪声的影响。
•边界处理:在使用 filter2D 时,需要注意边界处理方式。在这个示例中,使用了 BORDER_REPLICATE 边界条件来处理边界像素。
•掩模大小:Roberts算子的掩模大小为2x2,这意味着它只考虑了图像中的局部信息,因此对于精细的边缘检测可能不够精确。

通过这个示例,你应该能够理解如何在OpenCV中使用C++实现Roberts算子进行边缘检测。

运行结果3

cv::Mat kernelHorizontal(2, 2, CV_32F, robertsHorizontal); 该行代码还是错误

如果 cv::Mat(2, 2, CV_32F, robertsHorizontal) 这样的构造方式仍然报错,可能是因为 OpenCV 的 cv::Mat 构造函数并不直接接受一个外部的浮点数数组作为参数。在这种情况下,您可以先将数据复制到一个 std::vector<float> 中,然后使用 cv::Mat 的构造函数从 std::vector 创建矩阵,或者手动创建一个 cv::Mat 并使用 cv::Mat::operator=() 或 cv::Mat::copyTo() 方法来填充数据。

以下是两种可能的解决方案:
解决方案 1: 使用 std::vector<float>
#include <opencv2/opencv.hpp>
#include <vector>

int main() {
    // 定义Roberts交叉算子水平方向的内核
    static const float robertsHorizontal[] = { 1, 0, 0, -1 };

    // 将数据复制到一个std::vector中
    std::vector<float> data(robertsHorizontal, robertsHorizontal + 4);

    // 创建一个2x2的单通道浮点型矩阵
    cv::Mat kernelHorizontal = cv::Mat(2, 2, CV_32F, data.data());

    // 检查是否成功创建了内核
    if (kernelHorizontal.empty()) {
        std::cout << "Error: Matrix is empty!" << std::endl;
    } else {
        std::cout << "Kernel:\n" << kernelHorizontal << std::endl;
    }

    return 0;
}

解决方案 2: 手动创建并填充 cv::Mat
#include <opencv2/opencv.hpp>

int main() {
    // 定义Roberts交叉算子水平方向的内核
    static const float robertsHorizontal[] = { 1, 0, 0, -1 };

    // 创建一个2x2的单通道浮点型矩阵
    cv::Mat kernelHorizontal = cv::Mat::zeros(2, 2, CV_32F);

    // 填充矩阵
    kernelHorizontal.at<float>(0, 0) = robertsHorizontal[0];
    kernelHorizontal.at<float>(0, 1) = robertsHorizontal[1];
    kernelHorizontal.at<float>(1, 0) = robertsHorizontal[2];
    kernelHorizontal.at<float>(1, 1) = robertsHorizontal[3];

    // 检查是否成功创建了内核
    if (kernelHorizontal.empty()) {
        std::cout << "Error: Matrix is empty!" << std::endl;
    } else {
        std::cout << "Kernel:\n" << kernelHorizontal << std::endl;
    }

    return 0;
}
这两种方法都可以解决您的问题。选择哪种取决于您的具体需求和个人偏好。第一种方法更简洁一些,而第二种方法则提供了更多的控制权。

示例代码4

 

#include "pch.h"
#include <iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include <opencv2\imgproc\types_c.h>
#include<iostream>
using namespace std;
using namespace cv;
//#pragma comment(lib,"opencv_world450d.lib")
//Roberts算子实现
Mat roberts(Mat srcImage)
{
	Mat dstImage = srcImage.clone();
	int nRows = dstImage.rows;
	int nCols = dstImage.cols;
	for (int i = 0; i < nRows - 1; i++) {
		for (int j = 0; j < nCols - 1; j++) {
			//根据公式计算
			int t1 = (srcImage.at<uchar>(i, j) -
				srcImage.at<uchar>(i + 1, j + 1))*
				(srcImage.at<uchar>(i, j) -
					srcImage.at<uchar>(i + 1, j + 1));
			int t2 = (srcImage.at<uchar>(i + 1, j) -
				srcImage.at<uchar>(i, j + 1))*
				(srcImage.at<uchar>(i + 1, j) -
					srcImage.at<uchar>(i, j + 1));
			//计算g(x,y)
			dstImage.at<uchar>(i, j) = (uchar)sqrt(t1 + t2);
		}
	}
	return dstImage;
}

void main()
{
	Mat srcImage = imread("356.jpeg");
	if (!srcImage.data) {
		cout << "falied to read" << endl;
		system("pause");
		return;
	}
	Mat srcGray;
	cvtColor(srcImage, srcGray, CV_BGR2GRAY);
	//高斯滤波
	GaussianBlur(srcGray, srcGray, Size(3, 3),
		0, 0, BORDER_DEFAULT);
	Mat dstImage = roberts(srcGray);
	namedWindow("源图", WINDOW_NORMAL);
	namedWindow("边缘图", WINDOW_NORMAL);
	imshow("源图", srcImage);
	imshow("边缘图", dstImage);
	waitKey(0);
}

运行结果4

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

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

相关文章

飞睿智能UWB BLE Tag蓝牙防丢器模块,APP测距定位一键绑定,安全守护每一刻

我们总在不经意间与生活中的小物件擦肩而过——钥匙遗忘在咖啡厅的角落&#xff0c;钱包遗失在拥挤的地铁&#xff0c;甚至孩子的书包在人群中悄然消失……每一次的失而复得都是幸运的眷顾&#xff0c;但更多的是遗憾与不便。今天&#xff0c;就让我带你走进一个智能守护的新世…

【Python报错已解决】AttributeError: ‘WindowsPath‘ object has no attribute ‘rstrip‘

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

鸿蒙OpenHarmony【轻量系统芯片移植】轻量系统STM32F407芯片移植案例

轻量系统STM32F407芯片移植案例 介绍基于STM32F407IGT6芯片在拓维信息[Niobe407]开发板上移植OpenHarmony LiteOS-M轻量系统&#xff0c;提供交通、工业领域开发板解决方案。移植架构采用Board与SoC分离方案&#xff0c;使用arm gcc工具链Newlib C库&#xff0c;实现了lwip、l…

基于动态顺序表实现病历存储项目

基于动态顺序表实现通讯录项目https://blog.csdn.net/Eristic0618/article/details/135718230?spm1001.2014.3001.5506 原文在这里嗷&#xff0c;我进行了小小修改&#xff0c;快去关注这位佬。阿瑾0618https://blog.csdn.net/Eristic0618?typeblog &#xff08;1&#xff…

百度Android IM SDK组件能力建设及应用

作者 | 星途 导读 移动互联网时代&#xff0c;随着社交媒体、移动支付、线上购物等行业的快速发展&#xff0c;对即时通讯功能的需求不断增加。对于各APP而言&#xff0c;接入IM SDK&#xff08;即时通讯软件开发工具包&#xff09;能够大大降低开发成本、提高开发效率&#…

rocky9.2的lvs的NAT模式下的基本使用的详细示例

文章目录 前言什么是LVS?&#xff08;Linux Virtual Server&#xff09;LVS的组成1. 负载均衡器&#xff08;Load Balancer&#xff09;2. 后端服务器池&#xff08;Real Servers&#xff09;3. IPVS&#xff08;IP Virtual Server&#xff09;4. 调度算法&#xff08;Schedul…

Windows 常用的键盘快捷键总结

在日常工作或学习中&#xff0c;使用键盘快捷键不仅能够显著提高操作速度&#xff0c;还可以减少对鼠标的依赖&#xff0c;提升整体工作效率。Windows 操作系统为用户提供了众多功能强大的键盘快捷键&#xff0c;覆盖了不同方面&#xff0c;下面就给大家总结了常用的键盘快捷键…

15. 三数之和(左右指针)

算法分析&#xff1a; 数组排序&#xff1a;先将数组排序&#xff0c;时间复杂度 O(NlogN)。 固定一个数&#xff0c;双指针查找&#xff1a; 用一个循环固定第一个数 nums[i]。在剩余的部分&#xff0c;使用双指针 left 和 right 来寻找符合条件的三元组。 跳过重复元素(注意…

javascript-原型和原型链

原型 每个函数都有一个默认的原型对象 - prototype ,通过 prototype 我们可以扩展 js 的内置对象。一个函数和它创建的实例共享这个函数的原型属性和方法。实例对象的 constructor 会指向构造函数 原型链 每个实例对象都会有一个隐式原型属性 __proto__,通过 __proto__ 指…

单细胞BCR的分析Dandelion重注释的安装以及用法----11111

今天来学习下这个新的方法&#xff0c;主要是针对单细胞BCR 首先安装singularity Singularity 是一种容器化技术&#xff0c;类似于 Docker&#xff0c;专为高性能计算&#xff08;HPC&#xff09;和科学研究领域的需求设计。它允许用户在不同环境中运行和移植应用程序&#x…

【Canvas与诗词】《登科后》唐.孟郊

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>昔日龌龊不足夸</title><style type"text/css"&g…

线程 - 线程优缺点、线程自有和共享的数据、多线程使用公共空间、线程分离、线程库对线程的管理

文章目录 一、线程的优点1. 创建的代价2. 切换的代价缓存和进程/线程切换3. 占用的资源4. 效率二、线程的缺点1. 性能损失2. 健壮性降低3. 缺乏访问控制4. 编程难度高三、线程分离1. 线程分离2. pthread_detach ()① 函数细节② 函数使用四、线程自有和共享的数据1. 线程自有的…

[数据集][目标检测]无人机飞鸟检测数据集VOC+YOLO格式6647张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;6647 标注数量(xml文件个数)&#xff1a;6647 标注数量(txt文件个数)&#xff1a;6647 标注…

酒店布草洗涤-酒店分层管理编程实现--———未来之窗行业应用跨平台架构

一、添加楼层代码 未来之窗_人工智能_传送阵(添加楼层,客户信息,300,200) CyberWin_Dialog.layer(未来之窗传送,{type:"url",title:title,move:true,width:阵眼宽度"px",height:阵眼高度"px",id:未来之窗app_通用ID,mask:false,align:59,hidecl…

css 样式简单学习(一)

目录 1. css 介绍 1.1 css 样式 1.2 css代码风格 1.2.1 书写格式 1.2.2 样式大小写​编辑 1.2.3 空格规范 2. 基础选择器 2.1 选择器的作用​编辑 2.2 选择器的分类 2.3 基础选择器 2.3.1 标签选择器​编辑 2.3.2 类选择器​编辑 2.3.3 类选择器-多类名​编辑 2.…

Linux硬连接、软连接和复制的区别

‌硬连接、软连接和复制在Linux系统中的主要区别体现在以下三点&#xff1a; 文件链接的方式文件独立性文件系统的操作上。‌ 一、硬连接 1. 硬连接是通过ln命令创建的&#xff0c;它为文件创建别名&#xff0c;与源文件共享同一inode号码&#xff0c;因此硬连接和源文件实际…

松散绑定是什么?

概念 比如我的yml中写的last-name,这个和lastName是一样的&#xff0c;-后面跟着的字母默认是大写的&#xff0c;这就是松散绑定 示例 类代码&#xff1a; public class Person {private String lastName;private Integer age;private Boolean happy;private Date birth;pr…

c++与cmake:完整的C++项目构建注意事项

个人博客:Sekyoro的博客小屋 个人网站:Proanimer的个人网站 最近常常使用cmake构建c项目有感,从创建项目到打包发布总结一下需要注意的事情. 项目组织方式 具体的项目组织方式因人而异,这里推荐一种,在src目录中创建模块目录,再在include目录中常见对应的同名目录包含头文件,…

【Binlog实战】:基于Spring监听Binlog日志

【Binlog实战】&#xff1a;基于Spring监听Binlog日志 binlog的三种模式 MySQL 的二进制日志&#xff08;binlog&#xff09;有三种不同的格式&#xff0c;通常被称为 binlog 模式。这三种模式分别是 Statement 模式、Row 模式和Mixed 模式。 Statement 模式&#xff1a; 在 …

Redis存储原理

前言 我们从redis服务谈起&#xff0c;redis是单reactor&#xff0c;命令在redis-server线程处理。还有若干读写IO线程负责IO操作&#xff08;redis6.0之后&#xff0c;Redis之pipeline与事务&#xff09;。此外还有一个内存池线程负责内存管理、一个后台文件线程负责大文件的关…