OpenCV_距离变换的图像分割和Watershed算法详解

news2024/9/21 11:28:55

在学习watershed算法的时候,书写代码总会出现一些错误:

上述代码运行报错,显示OpenCV(4.10.0) Error: Assertion failed (src.type() == CV_8UC3 && dst.type() == CV_32SC1) in cv::watershed

查找资料:目前已解决

这个错误明确指出是在  watershed  函数中输入的参数类型不匹配。根据错误信息,要求  src (即  image  )的类型是  CV_8UC3  , dst (即  markers  )的类型是  CV_32SC1  。
 
您需要确保  image  是 8 位无符号的 3 通道彩色图像,并且  markers  是 32 位有符号的单通道矩阵。
 
请检查以下几点:
 
1.  image  的初始化和类型是否正确。
2.  markers  的创建和后续操作是否保持了  CV_32SC1  类型。
 
如果问题仍然存在,请提供更多关于  image  的初始化和加载的代码部分,以便更准确地帮助您找到问题所在。

分水岭算法

分水岭算法的基本原理为:将任意的灰度图像视为地形图表面,其中灰度值高的部分表示山峰和丘陵,而灰度值低的地方表示山谷。用不同颜色的水(标签)填充每个独立的山谷(局部最小值);随着水平面的上升,来自不同山谷(具有不同颜色)的水将开始合并。为了避免出现这种情况,需要在水汇合的位置建造水坝;持续填充水和建造水坝,直到所有的山峰和丘陵都在水下。整个过程中建造的水坝将作为图像分割的依据。

使用分水岭算法执行图像分割操作时通常包含下列步骤:
(1) 加载源图像并检查是否加载没有任何问题,然后显示
(2) 将原图转换为灰度图像,然后显示
(3) 应用形态学变换中的开运算和膨胀操作,去除图像噪声,获取图像边缘信息,确定图像背景
(4) 显示矩阵marks(只为显示,可省略)
(5) 执行分水岭算法分割图像
(6) 对每一个区域进行颜色填充
(7) 跟原始图像融合
//生成随机颜色函数
Vec3b randomColor(int value);
Vec3b randomColor(int value) {
	value = value % 255;
	RNG rng;
	int aa = rng.uniform(0, value);
	int bb = rng.uniform(0, value);
	int cc = rng.uniform(0, value);
	return Vec3b(aa, bb, cc);
}
void QuickDemo::thirtyFive(Mat& image) {
	//灰度化,滤波,Canny边缘检测
	Mat gray;
	cvtColor(image, gray, COLOR_BGR2GRAY);
	GaussianBlur(gray, gray, Size(5, 5), 2);
	Canny(gray, gray, 80, 150);
	//imshow("gray", gray);
	//查找轮廓
	vector<vector<Point>>contours;
	vector<Vec4i>hierarchy;
	findContours(gray, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());

	Mat contoursImage = Mat::zeros(image.size(), CV_8UC1);//轮廓	
	Mat marks(image.size(), CV_32S); //Opencv分水岭第二个矩阵参数
	marks = Scalar::all(0);
	//int index = 0;
	//int compCount = 0;
	for (size_t i = 0; i < contours.size(); i++)
	{
		//对marks进行标记,对不同区域的轮廓进行编号,相当于设置注水点,有多少轮廓,就有多少注水点
		drawContours(marks, contours, i, Scalar::all(i + 1), 1, 8, hierarchy);
		drawContours(contoursImage, contours, i, Scalar(255), 1, 8, hierarchy);
	}
	//我们来看一下传入的矩阵marks里是什么东西
	//Mat marksShows;
	//convertScaleAbs(marks,marksShows);
	//imshow("marksShows", marksShows);
	//imshow("contoursImage", contoursImage);
	watershed(image, marks);
	//我们再来看一下分水岭算法之后的矩阵marks里是什么东西
	Mat afterwatershed;
	convertScaleAbs(marks, afterwatershed);
	//imshow("afterwatershed", afterwatershed);
	//对每一个区域进行颜色填充
	Mat perspectiveImage = Mat::zeros(image.size(), CV_8UC3);
	for (size_t i = 0; i < marks.rows; i++)
	{
		for (size_t j = 0; j < marks.cols; j++)
		{
			int index = marks.at<int>(i, j);
			if (index == -1) {
				perspectiveImage.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
			}
			else {
				perspectiveImage.at<Vec3b>(i, j) = randomColor(index);

			}
		}
	}
	imshow("perspectiveImage", perspectiveImage);
	//分割并填充颜色的结果跟原始图像融合
	Mat wshed;
	addWeighted(image, 0.4, perspectiveImage, 0.6, 0, wshed);
	imshow("wshed", wshed);


}

说明/结果

1.加载源图像并检查是否加载没有任何问题,然后显示:

2.将原图转换为灰度图像,然后显示:

Mat gray;
cvtColor(image, gray, COLOR_BGR2GRAY);
imshow("gray", gray);

3.应用形态学变换中的开运算和膨胀操作,去除图像噪声,获取图像边缘信息,确定图像背景

GaussianBlur(gray, gray, Size(5, 5), 2);
Canny(gray, gray, 80, 150);
imshow("gray", gray);
	
//查找轮廓
vector<vector<Point>>contours;
vector<Vec4i>hierarchy;
findContours(gray, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
	
Mat contoursImage = Mat::zeros(image.size(), CV_8UC1);//轮廓	
Mat marks(image.size(), CV_32S); //Opencv分水岭第二个矩阵参数
marks = Scalar::all(0);
//int index = 0;
//int compCount = 0;
for (size_t i = 0; i < contours.size(); i++)
{
	//对marks进行标记,对不同区域的轮廓进行编号,相当于设置注水点,有多少轮廓,就有多少注水点
	drawContours(marks, contours, i, Scalar::all(i + 1), 1, 8, hierarchy);
	drawContours(contoursImage, contours, i, Scalar(255), 1, 8, hierarchy);
}
imshow("contoursImage", contoursImage);

4.显示矩阵marks(只为显示,可省略)

//我们来看一下传入的矩阵marks里是什么东西
Mat marksShows;
convertScaleAbs(marks,marksShows);
imshow("marksShows", marksShows);

5.执行分水岭算法分割图像

watershed(image, marks);
//我们再来看一下分水岭算法之后的矩阵marks里是什么东西
Mat afterwatershed;
convertScaleAbs(marks, afterwatershed);
imshow("afterwatershed", afterwatershed);

6.对每一个区域进行颜色填充

//生成随机颜色函数
Vec3b randomColor(int value);
Vec3b randomColor(int value) {
	value = value % 255;
	RNG rng;
	int aa = rng.uniform(0, value);
	int bb = rng.uniform(0, value);
	int cc = rng.uniform(0, value);
	return Vec3b(aa, bb, cc);
}
//对每一个区域进行颜色填充
Mat perspectiveImage = Mat::zeros(image.size(), CV_8UC3);
for (size_t i = 0; i < marks.rows; i++)
{
	for (size_t j = 0; j < marks.cols; j++)
	{
		int index = marks.at<int>(i, j);
		if (index == -1) {
			perspectiveImage.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
		}
		else {
			perspectiveImage.at<Vec3b>(i, j) = randomColor(index);
		}
	}
}
imshow("perspectiveImage", perspectiveImage);

7.跟原始图像融合

Mat wshed;
addWeighted(image, 0.4, perspectiveImage, 0.6, 0, wshed);
imshow("wshed", wshed);

距离变换实现分水岭算法

实现步骤:
1.输入图像
2.灰度化
3.二值化
4.执行距离变换
5.归一化
6.二值化
7.生成marker:通过findContours+drawContours来创建一个marker
8.将7生成的marker放入分水岭函数:watershed
9.给marker着色
10.输出着色后的图像
此算法关键点在于生成marker。生成marker之后其实已经完成了算法,后面的着色只是为了让输出更加好看。
void QuickDemo::thirtyFour(Mat& image) {

	Mat gray;
	cvtColor(image, gray, COLOR_BGR2GRAY);

	Mat binary;
	threshold(gray, binary, 175, 255, THRESH_BINARY);
	//imshow("binary image ",bw);

	Mat dist;
	distanceTransform(binary, dist, DIST_L2, 3);
	normalize(dist, dist, 0, 1, NORM_MINMAX);

	threshold(dist, dist, 0.1, 1, THRESH_BINARY);
	normalize(dist, dist, 0, 255, NORM_MINMAX);
	dist.convertTo(dist, CV_8UC1);
	imshow("distanceTransform", dist);

	vector<vector<Point>>contours;
	findContours(dist, contours, RETR_CCOMP, CHAIN_APPROX_SIMPLE, Point());
	vector<Vec4i>hierarchy;
	Mat markers(dist.size(), CV_8U); //Opencv分水岭第二个矩阵参数
	markers = Scalar::all(0);
	for (size_t i = 0; i < contours.size(); i++)
	{
		drawContours(markers, contours, i, Scalar::all(i + 1), -1, 8, hierarchy, INT_MAX);
	}
	circle(markers, Point(3, 3), 3, Scalar(255), -1);
	//imshow("markers", markers*20);
	image.convertTo(image, CV_8UC3);
	markers.convertTo(markers, CV_32SC1);
	watershed(image, markers);
	markers.convertTo(markers, CV_8UC1);
	imshow("markers", markers*50);

	RNG rng(12345);
	vector<Vec3b>colors;
	for (size_t i = 0; i < contours.size(); i++)
	{
		int r = rng.uniform(0, 255);
		int g = rng.uniform(0, 255);
		int b = rng.uniform(0, 255);
		colors.push_back(Vec3b(r, g, b));
	}
	//给marker着色
	Mat finalResult = Mat::zeros(dist.size(), CV_8UC3);//三通道彩色图像
	int index = 0;
	for (int row = 0; row < markers.rows; row++) {
		for (int col = 0; col < markers.cols; col++) {
			index = markers.at<uchar>(row, col);
			if (index > 0 && index <= contours.size()) {
				finalResult.at<Vec3b>(row, col) = colors[index - 1];
			}
			else {
				finalResult.at<Vec3b>(row, col) = Vec3b(255, 255, 255);
			}
		}
	}
	imshow("finalResult", finalResult);

}

说明/结果

1. 输入图像并显示

2.获取灰度图像并显示

Mat gray;
cvtColor(image, gray, COLOR_BGR2GRAY);
imshow("gray image ", gray);

3.获取二值图像并显示

Mat binary;
threshold(gray, binary, 175, 255, THRESH_BINARY);
imshow("binary image ", binary);

4.执行距离变换归一化,并显示

Mat dist;
distanceTransform(binary, dist, DIST_L2, 3);
normalize(dist, dist, 0, 1, NORM_MINMAX);
imshow("dist image ", dist);

5.进行二值变换归一化,并显示

threshold(dist, dist, 0.1, 1, THRESH_BINARY);
normalize(dist, dist, 0, 255, NORM_MINMAX);
dist.convertTo(dist, CV_8UC1);
imshow("distanceTransform", dist);

6.生成marker:通过findContours+drawContours来创建一个marker

vector<vector<Point>>contours;
findContours(dist, contours, RETR_CCOMP, CHAIN_APPROX_SIMPLE, Point());
vector<Vec4i>hierarchy;
Mat markers(dist.size(), CV_8U); //Opencv分水岭第二个矩阵参数
markers = Scalar::all(0);
for (size_t i = 0; i < contours.size(); i++)
{
	drawContours(markers, contours, i, Scalar::all(i + 1), -1, 8, hierarchy, INT_MAX);
}
circle(markers, Point(3, 3), 3, Scalar(255), -1);
imshow("markers", markers*20);

7.将7生成的marker放入分水岭函数:watershed

image.convertTo(image, CV_8UC3);
markers.convertTo(markers, CV_32SC1);
watershed(image, markers);
markers.convertTo(markers, CV_8UC1);
imshow("markers", markers*50);

8.给marker着色,并输出着色后的图像

RNG rng(12345);
vector<Vec3b>colors;
for (size_t i = 0; i < contours.size(); i++)
{
	int r = rng.uniform(0, 255);
	int g = rng.uniform(0, 255);
	int b = rng.uniform(0, 255);
	colors.push_back(Vec3b(r, g, b));	
}
//给marker着色
Mat finalResult = Mat::zeros(dist.size(), CV_8UC3);//三通道彩色图像
int index = 0;
for (int row = 0; row < markers.rows; row++) {
	for (int col = 0; col < markers.cols; col++) {
		index = markers.at<uchar>(row, col);
		if (index > 0 && index <= contours.size()) {
			finalResult.at<Vec3b>(row, col) = colors[index - 1];
		}
		else {
			finalResult.at<Vec3b>(row, col) = Vec3b(255, 255, 255);
		}
	}
}
imshow("finalResult", finalResult);

9.跟原始图像融合

Mat wdst;
addWeighted(finalResult,0.6,image,0.4,0, wdst);
imshow("wdst", wdst);

threshold函数

threshold( 
    InputArray src, 
    OutputArray dst, 
    double thresh, 
    double maxval,
    type 
);
//参数1:输入的灰度图像
//参数2:输出图像
//参数3:进行阈值操作时阈值的大小
//参数4:设定的最大灰度值(该参数运用在二进制与反二进制阈值操作中)
//参数5:阈值的类型。从下面提到的5种中选择出的结果
THRESH_BINARY=0: 二进制阈值
THRESH_BINARY_INV=1: 反二进制阈值
THRESH_TRUNC=2: 截断阈值
THRESH_TOZERO=3: 0阈值
THRESH_TOZERO_INV=4: 反0阈值
THRESH_OTSU=8   自适应閾值
 

(1)正向二值化,THRESH_BINARY

正向二值化,如果当前的像素值大于设置的阈值(thresh),则将该点的像素值设置为maxval;否则,将该点的像素值设置为0;

(2)反向二值化,THRESH_BINARY_INV

反向二值化,如果当前的像素值大于设置的阈值(thresh),则将该点的像素值设置为0;否则,将该点的像素值设置为maxval

(3)THRESH_TRUNC

如果当前的像素值大于设置的阈值(thresh),则将该点的像素值设置为threshold;否则,将该点的像素值不变

(4)THRESH_TOZERO

如果当前的像素值大于设置的阈值(thresh),则将该点的像素值不变;否则,将该点的像素值设置为0

THRESH_TOZERO_INV

如果当前的像素值大于设置的阈值(thresh),则将该点的像素值设置为0;否则,将该点的像素值不变

adaptiveThreshold函数

void adaptiveThreshold(
	InputArray src,
    OutputArray dst,
    double maxValue,
    int adaptiveMethod,
    int thresholdType,
    int blockSize,
    double C
);
第一个参数,InputArray src,原图,即输入图像,是一个8位单通道的图像;
第二个参数,OutputArray dst,目标图像,与原图像具有同样的尺寸与类型;
第三个参数,double maxValue,分配给满足条件的像素的非零值;
第四个参数,int adaptiveMethod,自适应阈值的方法,通常有以下几种方法;
	ADAPTIVE_THRESH_MEAN_C,阈值T(x,y)是(x,y)减去C的Blocksize×Blocksize
邻域的平均值。
	ADAPTIVE_THRESH_GAUSSIAN_C ,阈值T(x,y)是(x,y)减去C的Blocksize×Blocksize
邻域的加权和(与高斯相关),默认sigma(标准差)用于指定的Blocksize;具体的情况可以参见getGaussianKernel函数;
第五个参数,int thresholdType,阈值的类型必须是以下两种类型,
	THRESH_BINARY,正向二值化
	THRESH_BINARY_INV ,反向二值化
第六个参数,int blockSize,计算blocksize x blocksize大小的领域内的阈值,必须为奇数,
例如,3,5,7等等,一般二值化使用21,31,41;
第七个参数,double C,从平均数或加权平均数减去常量。通常,它是正的,但也可能是零或负数。
二值化时使用的7。
补充
函数cvAdaptiveThreshold的确可以将灰度图像二值化,但它的主要功能应该是边缘提取,
关键是里面的block_size参数,该参数是决定局部阈值的block的大小
1)当block很小时,如block_size=3 or 5 or 7时,“自适应”的程度很高,
即容易出现block里面的像素值都差不多,这样便无法二值化,而只能在边缘等梯度大的地方实现二值化
,结果显得它是边缘提取函数;
2)当把block_size设为比较大的值时,如block_size=21 or 31 or 41时,cvAdaptiveThreshold便是
二值化函数了;
3)src与dst 这两个都要是单通道的图像。

findContours函数

void findContours (
    InputOutputArray      image,       // 输入图像
    OutputArrayOfArrays   contours,    // 检测到的轮廓
    OutputArray           hierarchy,   // 可选的输出向量
    int       mode,            
// 轮廓获取模式 (RETR_EXTERNAL, RETR_LIST, RETR_CCOMP,RETR_TREE, RETR_FLOODFILL)
    int       method,          
// 轮廓近似算法 (CHAIN_APPROX_NONE, CHAIN_APPROX_SIMPLE, CHAIN_APPROX_TC89_L1, CHAIN_APPROX_TC89_KCOS)
    Point     offset = Point() // 轮廓偏移量
)

hierarchy 为可选的参数,如果不选择该参数,则可得到 findContours 函数的第二种形式
void findContours (
  InputOutputArray   image,
  OutputArrayOfArrays contours,
  int    mode,
  int    method,
  Point   offset = Point()
)

drawContours() 函数如下: 
void drawContours (
    InputOutputArray     image,         // 目标图像
    InputArrayOfArrays   contours,      // 所有的输入轮廓
    int               contourIdx,      //
    const Scalar &     color,           //  轮廓颜色
    int          thickness = 1,         //  轮廓线厚度
    int          lineType = LINE_8,     //
    InputArray   hierarchy = noArray(), //
    int          maxLevel = INT_MAX,    //
    Point        offset = Point()       //    
)

 watershed函数

watershed(src,markers);
src:原图像
markers:目标markers,生成markers是通过findContours边沿查找+drawContours来实现的。
ps:这一步非常重要,有了marker就可以使用分水岭算法了。

distanceTransform函数 

distanceTransform()距离变换的定义是计算一个图像中非零像素点到最近的零像素点的
距离,也就是到零像素点的最短距离。即距离变换的定义是计算一个图像中非零像素点到
最近的零像素点的距离,也就是到零像素点的最短距离。
通常处理的是一个二值化的图,所以求距离可以归一化,距离(像素距离)单位为1。
void distanceTransform(
    InputArray src, 
    OutputArray dst, 
    int distanceType, 
    int maskSize, 
    int dstType=CV_32F )
void distanceTransform(
    InputArray src, 
    OutputArray dst, 
    OutputArray labels, 
    int distanceType, 
    int maskSize, 
    int labelType=DIST_LABEL_CCOMP )
src:源矩阵
dst:目标矩阵
distanceType:距离类型。可以的类型是CV_DIST_L1、CV_DIST_L2、CV_DIST_C,具体各
类型的意义,请查阅相关算法文档。
maskSize:距离变换运算时的掩码大小。值可以是3、5或CV_DIST_MASK_PRECISE
(5或CV_DIST_MASK_PRECISE只能用在第一个原型中)。
当distanceType=CV_DIST_L1 或 CV_DIST_C时,maskSize只能为3。
dstType:输出图像(矩阵)的类型,可以是CV_8U 或 CV_32F。CV_8U只能用在第一个原型中,
而且distanceType只能为CV_DIST_L1。
labels:输出二维阵列标签。
labelType:标签数组类型。可选值为DIST_LABEL_CCOMP和DIST_LABEL_PIXEL

原图片

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

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

相关文章

idea 编辑器常用插件集合

SequenceDiagram 用于生成时序图的插件&#xff0c;支持一键生成功能。 使用&#xff1a;选择某个具体的方法&#xff0c;点击右键菜单&#xff0c;选择“Sequence Diagram” 便可生成相应的时序图 例子&#xff1a; 效果&#xff1a; Code Iris Code Iris可以根据代码自动…

c++day3 手动封装一个顺序表(SeqList),分文件编译实现

要求: 有私有成员&#xff1a;顺序表数组的起始地址 ptr、 顺序表的总长度&#xff1a;size、顺序表的实际长度&#xff1a;len 成员函数&#xff1a;初始化 init(int n) 判空&#xff1a;empty 判满&#xff1a;full 尾插&#xff1a;push_back 插入&#xff1a;insert&…

优数:助力更高效的边缘计算

在数字化时代的浪潮中&#xff0c;数据已成为企业最宝贵的资产之一。随着物联网&#xff08;IoT&#xff09;设备的激增和5G技术的兴起&#xff0c;我们正迅速步入一个新时代&#xff0c;在这个时代中&#xff0c;数据不仅在量上爆炸性增长&#xff0c;更在速度和实时性上提出了…

Hadoop里面MapReduce的序列化与Java序列化比较

什么是序列化&#xff1f; jvm中的一个对象&#xff0c;不是类&#xff0c;假如你想把一个对象&#xff0c;保存到磁盘上&#xff0c;必须序列化&#xff0c;你把文件中的对象进行恢复&#xff0c;是不是的反序列化。 假如你想把对象发送给另一个服务器&#xff0c;需要通过网…

线性dp 总结详解

就是感觉之前 dp 的 blog 太乱了整理一下。 LIS(最长上升子序列) 例题 给定一个整数序列&#xff0c;找到它的所有严格递增子序列中最长的序列&#xff0c;输出其长度。 思路 拿到题目&#xff0c;大家第一时间想到的应该是的暴力(dp)做法&#xff1a; #include <bits/s…

基于Windows系统以tomcat为案例,讲解如何新增自启动服务,定时重启服务。

文章目录 引言I 设置服务自启动的常规操作II 安装多个tomcat服务,并设置自启动。III 定时重启服务引言 为了同一个版本安装多个tomcat服务,并设置自启动。使用Windows的任务计划程序来创建一个定时任务,用于重启Tomcat服务。I 设置服务自启动的常规操作 运行窗口输入control…

2024双11有哪些值得入手的好物?2024年双十一好物推荐

随着2024年双十一购物狂欢节的临近&#xff0c;消费者们正摩拳擦掌&#xff0c;准备迎接这场年度最大的网购盛会。面对琳琅满目的促销信息和令人眼花缭乱的商品&#xff0c;如何在海量商品中精准锁定那些真正值得购买的好物&#xff0c;成为每位精明买家的首要任务。本文旨在为…

牛啊,GitHub 代理加速图文教程

大家好&#xff0c;众所周知&#xff0c;GitHub 在国内访问速度堪忧&#xff0c;经常出现访问不了的情况&#xff0c;如果我们去 clone 代码&#xff0c;网速非常差。今天教大家如何给 GitHub 进行加速。 要用到我开发的开源项目 Cloudflare Workers Proxy&#xff0c;它是一个…

视频压缩篇:适用于 Windows 的 10 款最佳视频压缩器

视频压缩器现在对许多想要减小视频大小的视频编辑者来说非常有名。但是&#xff0c;并非所有可以在网上找到的视频压缩器都能产生最佳输出。因此&#xff0c;我们搜索了可以无损压缩视频的最出色的视频压缩器应用程序。本文列出了您可以在离线、在线和手机上使用的十大最佳视频…

2024华为杯研赛D题保姆级教程思路分析+教程

2024年中国研究生数学建模竞赛D题保姆级教程思路分析 D题&#xff1a;大数据驱动的地理综合问题&#xff08;数学分析&#xff0c;统计学&#xff09; 关键词&#xff1a;地理、气候、统计&#xff08;细致到此题&#xff1a;统计指标、统计模型、统计结果解释&#xff09; …

无线领夹麦克风哪个降噪好?一文搞懂麦克风什么牌子的音质效果好

对于视频拍摄、直播来说&#xff0c;一款好的拾音麦克风是不可或缺的。作为一位数码博主&#xff0c;也是会经常拍摄视频讲解&#xff0c;早期没有使用麦克风时&#xff0c;声音不够清晰&#xff0c;而且周围环境音也会同时被收录&#xff0c;导致整个音频的音质效果极差&#…

【多线程】CAS的原理及应用,看这篇文章就够啦

&#x1f490;个人主页&#xff1a;初晴~ &#x1f4da;相关专栏&#xff1a;多线程 / javaEE初阶 一、CAS概述 CAS&#xff08;Compare and Swap&#xff09;&#xff0c;中文译为 “比较并交换” &#xff0c;是一种无锁算法中常用的原子操作。CAS通常用于实现线程之间的同…

力扣之1459.矩形面积

1. 1459.矩形面积 1.1 题干 表: Points ---------------------- | Column Name | Type | ---------------------- | id | int | | x_value | int | | y_value | int | ---------------------- id 是该表中具有唯一值的列。 每个点都用二维坐标 (x_value, y_value) 表示。 编…

【力扣每日一题——2374. 边积分最高的节点】python

2374. 边积分最高的节点 给你一个有向图&#xff0c;图中有 n 个节点&#xff0c;节点编号从 0 到 n - 1 &#xff0c;其中每个节点都 恰有一条 出边。 图由一个下标从 0 开始、长度为 n 的整数数组 edges 表示&#xff0c;其中 edges[i] 表示存在一条从节点 i 到节点 edges[…

大模型训练实战经验总结

在当今AI技术飞速发展的背景下&#xff0c;定制化大模型的自主训练已成为满足特定行业需求、保障数据安全、提升模型应用效能的关键途径。本文将深度剖析这一过程的核心价值与实践智慧&#xff0c;从数据隐私保护、模型透明度增强&#xff0c;到数据预处理的精细操作&#xff0…

记录一次fs配置导致串线的问题

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 fs在实际的使用过程中也会经常碰到莫名其妙的问题&#xff0c;大部分都是配置问题。 环境 CentOS 7.9 freeswitch 1.10.7 docker 26.1.1 问题描述 组网方案如下。其中的fs-reg是注册服务器&#xff0c;fs1和fs2是…

NEES(Normalized Estimation Error Squared 归一化估计误差平方)

目录 NEES的计算步骤 解释 示例 代码 与RMSE的区别 NEES(Normalized Estimation Error Squared)是一种用于评估状态估计精度的指标,通常用于比较估计值与真实值之间的差异。计算NEES的步骤如下: NEES的计算步骤 获取状态估计: 设定目标的真实状态为。设定状态估计为…

Ubuntu 20.04安装pycharm2022及配置快捷方式

一、下载与安装 1. 下载 在 官网 下载所需版本&#xff0c;如&#xff1a;下载 2022.3.3 - Linux (tar.gz) 2. 安装 设置自定义安装路径(推荐在 /opt/ 路径下)并安装 mkdir -p ~/Documents/software/pycharm/ cd ~/Documents/software/pycharm/ mv ~/Downloads/pycharm-c…

使用 Puppeteer-Cluster 和代理进行高效网络抓取: 完全指南

文章目录 一、介绍&#xff1f;二、什么是 Puppeteer-Cluster&#xff1f;三、为什么代理在网络抓取中很重要&#xff1f;四、 为什么使用带代理的 Puppeteer-Cluster&#xff1f;五、分步指南&#xff1a; 带代理的 Puppeteer 群集5.1. 步骤 1&#xff1a;安装所需程序库5.2. …

光耦知识分享 | 可控硅光耦的行业应用及封装形式

可控硅光耦&#xff08;SCR Optocoupler&#xff09;是一种特殊类型的光耦&#xff0c;通常由红外发光二极管&#xff08;LED&#xff09;和双向可控硅&#xff08;SCR&#xff09;组成&#xff0c;用于实现输入和输出之间的电气隔离和信号传输。能够以最少的外部元器件数控制大…