opencv学习二值分析

内容来源于《opencv4应用开发入门、进阶与工程化实践》 

二值分析:

常见的二值化方法:

  • 基于全局阈值(threshold)得到的二值图像;
  • 基于自适应阈值(adaptiveThreshold)得到的二值图像;
  • 边缘检测(Canny)
  • 基于像素值范围(inRange)

threshold 

thresholdType介绍:

  • THRESH_BINARY表示大于thresh的取maxval,否则取0;
  • THRESH_BINARY_INV表示大于thresh的取0,否则取maxvalue;
  • THRESH_TRUNC表示大于threshthreshold,否则不改变灰度值;
  • THRESH_TOZERO表示大于thresh的不改变灰度值,否则取0;
  • THRESH_TOZERO_INV表示大于thresh取0,窦泽不改变灰度值;
  • THRESH_OTSU表示使用otsu自动计算阈值;
  • THRESH_TRIANGLE表示使用Triangle自动计算阈值;

adaptiveThreshold

void adaptiveThreshold( InputArray src, OutputArray dst,
                       double maxValue, int adaptiveMethod,
                       int thresholdType, int blockSize, double C );
  • src表示需要进行二值化的图像;需要注意的是,该输入必须是8-bit单通道的图像;
  • dst表示输出图像的二值图像;
  • maxValue是一个非零值,用于对哪些满足条件的阈值进行赋值;
  • adaptiveMethod表示选择哪一种自适应阈值算法;Opencv提供两种,ADAPTIVE_THRESH_MEAN_CADAPTIVE_THRESH_GAUSSIAN_C,下面会详细介绍;
  • thresholdType表示二值化类型,OpenCV提供两种, THRESH_BINARYTHRESH_BINARY_INV,下面会详细介绍;
  • blocksize表示参与计算的像素的领域范围,必须使用奇数;
  • C可以为正数, 零或者负数;用于在计算过程中容忍程度;
thresholdType介绍

adaptiveMethod介绍

第一种ADAPTIVE_THRESH_MEAN_C,针对像素(x,y)的计算方式如下:

  • T(x,y)结果是在(x,y)的邻域blockSize×blockSize范围内所有灰度值的均值减去C�;

第二种ADAPTIVE_THRESH_GAUSSIAN_C,针对像素(x,y)的计算方式如下:

  • 首先,生成一个大小为blockSize×blockSize的高斯核,作为权重;
  • 其次,利用高斯核与(x,y)邻域范围内灰度值,进行加权求和,再减去C,得到T(x,y);

高斯核:符合高斯分布,距离越近权重越大。 

Canny

标准的边缘检测算法包括如下几步:

  1. 将图像转为灰度图像
  2. 通过高斯模糊卷积实现降噪
  3. 计算图像梯度的大小与角度
  4. 非最大信号压制
  5. 双阈值边缘连接

在图像利用Sobel算子(也是滤波函数)计算x, y两个方向的梯度: 

 其次,计算梯度的强度和方向:(根据X轴和Y轴方向的梯度可以计算图像中像素点的梯度幅值G与角度θ)

非最大抑制 

理想情况下只有边缘像素的梯度是大于阈值T的,但实际情况下,局部也会出现多个高梯度阈值,所以要需要每个像素根据自身角度方向与两侧像素梯度值进行比较,如果当前像素点的梯度值小于两侧像素的梯度值,则将当前像素点的值设置为0;如果大于两侧像素的梯度值则保留。

双阈值连接

双阈值连接时保证边缘连续的关键步骤。一个高阈值H,一个低阈值L。

双阈值连接首先使用L对梯度图像进行处理,高于L保留,低于L丢弃,并将值设为零。然后使用H进行处理,高于H都视为边缘像素点。梯度值在[L,H]之间的:如果从低阈值像素点出发,最终可以通过相邻的像素点连接到高阈值像素点,而且整个连线上的像素点梯度值都大于L,则保留;否则设置为0。

轮廓发现与轮廓绘制

轮廓发现:

void findContours//提取轮廓,用于提取图像的轮廓
(
InputOutputArray image,//输入图像,必须是8位单通道图像,并且应该转化成二值的
OutputArrayOfArrays contours,//检测到的轮廓,每个轮廓被表示成一个point向量
OutputArray hierarchy,//可选的输出向量,包含图像的拓扑信息。其中元素的个数和检测到的轮廓的数量相等
int mode,//说明需要的轮廓类型和希望的返回值方式
int method,//轮廓近似方法
Point offset = Point()
)

轮廓绘制:

void drawContours//绘制轮廓,用于绘制找到的图像轮廓
(
 InputOutputArray image,//要绘制轮廓的图像
 InputArrayOfArrays contours,//所有输入的轮廓,每个轮廓被保存成一个point向量
 int contourIdx,//指定要绘制轮廓的编号,如果是负数,则绘制所有的轮廓
 const Scalar& color,//绘制轮廓所用的颜色
 int thickness = 1, //绘制轮廓的线的粗细,如果是负数,则轮廓内部被填充
 int lineType = 8, /绘制轮廓的线的连通性
 InputArray hierarchy = noArray(),//关于层级的可选参数,只有绘制部分轮廓时才会用到
 int maxLevel = INT_MAX,//绘制轮廓的最高级别,这个参数只有hierarchy有效的时候才有效
                                          //maxLevel=0,绘制与输入轮廓属于同一等级的所有轮廓即输入轮廓和与其相邻的轮廓
                                          //maxLevel=1, 绘制与输入轮廓同一等级的所有轮廓与其子节点。
                                          //maxLevel=2,绘制与输入轮廓同一等级的所有轮廓与其子节点以及子节点的子节点
 Point offset = Point()
)

代码示例:

void BinaryAnalysis::find_contours_demo(Mat &image) {
	Mat gray;
	cvtColor(image, gray, COLOR_BGR2GRAY);
	std::vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(gray, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
	Mat result = Mat::zeros(image.size(), image.type());
	drawContours(result, contours, -1, Scalar(0, 0, 255), 2, 8);
	imshow("轮廓发现", result);
}

轮廓测量

轮廓测量指对二值图像的每个轮廓的弧长和面积进行测量,根据轮廓的面积和弧长对大小不同的对象实现查找、过滤与处理的操作,以寻找感兴趣的RoI区域。

计算面积:

//计算面积
double cv::contourArea(
    InputArray contour
    bool oriented = false
)
//计算弧长、周长
double cv::arcLength(
    InputArray curve
    bool closed
    )
//计算轮廓外接矩形
Rect cv::boundingRect(
    InputArray array
)
//示例代码
void BinaryAnalysis::contours_analysis_demo(Mat &image) {
	// 二值化
	Mat gray, binary;
	cvtColor(image, gray, COLOR_BGR2GRAY);
	double t = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);

	// 轮廓发现
	std::vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
	Mat result = Mat::zeros(image.size(), image.type());
	drawContours(result, contours, -1, Scalar(0, 0, 255), 2, 8);

	// 轮廓测量
	for (size_t t = 0; t < contours.size(); t++) {
		Rect box = boundingRect(contours[t]);
		
		double area = contourArea(contours[t]);
		double arc = arcLength(contours[t], true);
		putText(result, format("area:%.2f", area), box.tl(), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 255, 0), 1, 8);
		putText(result, format("arc:%.2f", arc), Point(box.x, box.y+14), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 255, 0), 1, 8);
	}
	imshow("轮廓测量", result);
}

轮廓拟合逼近

//拟合椭圆
RotatedRect cv::fitEllipse(
    InputArray
)
//拟合直线
void cv::fitLine(
    InputArray points
    OutputArray line
    int distType,
    double param,
    double reps,
    double aeps
)//distType表示拟合时使用的距离计算公式、、param表示对模型进行拟合距离计算的公式是否需要用到该参数。当distType参数为5,6,7时表示需要用到该参数,否则该参数不参与拟合距离计算、、reps与aeps表示对拟合结果的精度要求
//轮廓逼近
void cv::approxPolyDP(
    InputArray curve
    OutputArray approxCurve
    double epsilon
    bool closed
)

轮廓椭圆拟合与直线拟合示例代码:

//示例代码
void BinaryAnalysis::contours_fitness_demo(Mat &image) {
	// 二值化
	Mat edges;
	int t = 80;
	Canny(image, edges, t, t * 2, 3, false);
    //返回一个结构元素(卷积核),主要用于后续的膨胀腐蚀操作
	Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
	//膨胀函数对于3*3的核,取最大的数放在中心位置
    dilate(edges, edges, k);

	// 轮廓发现
	std::vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(edges, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());

	for (size_t t = 0; t < contours.size(); t++) {
		if (contours[t].size() < 5) {
			continue;
		}
		// 拟合椭圆
		// RotatedRect rrt = fitEllipse(contours[t]);
		// ellipse(image, rrt, Scalar(0, 0, 255), 2, 8);

		// 拟合直线
		Vec4f oneline;
		fitLine(contours[t], oneline, DIST_L1, 0, 0.01, 0.01);
		float vx = oneline[0];
		float vy = oneline[1];
		float x0 = oneline[2];
		float y0 = oneline[3];

		// 直线参数斜率k与截矩b
		float k = vy / vx;
		float b = y0 - k*x0;
		// 寻找轮廓极值点
		int minx = 0, miny = 10000;
		int maxx = 0, maxy = 0;
		for (int i = 0; i < contours[t].size(); i++) {
			Point pt = contours[t][i];
			if (miny > pt.y) {
				miny = pt.y;
			}
			if (maxy < pt.y) {
				maxy = pt.y;
			}
		}
		maxx = (maxy - b) / k;
		minx = (miny - b) / k;
		line(image, Point(maxx, maxy), Point(minx, miny), Scalar(0, 0, 255), 2, 8, 0);
	}
	imshow("轮廓拟合-直线拟合", image);
}

轮廓逼近示例代码:

void BinaryAnalysis::contours_apprv_demo(Mat &image) {
	Mat gray, binary;
	cvtColor(image, gray, COLOR_BGR2GRAY);
	double t = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
	std::vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
	for (size_t t = 0; t < contours.size(); t++) {
		std::vector<Point> pts;
		approxPolyDP(contours[t], pts, 10, true);
		for (int i = 0; i < pts.size(); i++) {
			circle(image, pts[i], 3, Scalar(0, 0, 255), 2, 8, 0);
		}
	}
	imshow("轮廓逼近", image);
}

轮廓分析

可以根据轮廓发现得到的每个对象轮廓的最大外接矩形活最小外接矩形计算其横纵比,周长,面积。

下面的示例代码使用了轮廓测量与分析的相关函数,完成了对每个对象轮廓的分析,得到了每个对象轮廓的属性输出。

void BinaryAnalysis::contours_attrs_demo(Mat &image) {
	// 二值化
	Mat edges;
	int t = 80;
	Canny(image, edges, t, t * 2, 3, false);

	Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
	dilate(edges, edges, k);

	// 轮廓发现
	std::vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(edges, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
	Mat result = Mat::zeros(image.size(), image.type());
	drawContours(result, contours, -1, Scalar(0, 0, 255), 2, 8);
	imshow("轮廓发现", result);

	Mat mask = Mat::zeros(image.size(), CV_8UC1);
	for (size_t t = 0; t < contours.size(); t++) {
		Rect box = boundingRect(contours[t]);
		RotatedRect rrt = minAreaRect(contours[t]);
		std::vector<Point> hulls;
		convexHull(contours[t], hulls);
		double hull_area = contourArea(hulls);
		double box_area = box.width*box.height;
		double area = contourArea(contours[t]);
		// 计算横纵比
		double aspect_ratio = saturate_cast<double>(rrt.size.width) / saturate_cast<double>(rrt.size.height);
		// 计算延展度
		double extent = area / box_area;
		// 计算实密度
		double solidity = area / hull_area;
		// 生成mask与计算像素均值
		mask.setTo(Scalar(0));
		drawContours(mask, contours, t, Scalar(255), -1);
		Scalar bgra = mean(image, mask);
		putText(image, format("extent:%.2f", extent), box.tl(), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 0, 255), 1, 8);
		putText(image, format("solidity:%.2f", solidity), Point(box.x, box.y + 14), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 0, 255), 1, 8);
		putText(image, format("aspect_ratio:%.2f", aspect_ratio), Point(box.x, box.y + 28), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 0, 255), 1, 8);
		putText(image, format("mean:(%d,%d,%d)", (int)bgra[0], (int)bgra[1], (int)bgra[2]), Point(box.x, box.y + 42), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 0, 255), 1, 8);
	}
	imshow("轮廓分析", image);
}

直线检测圆检测

在二值图像分析中,直线检测和轮廓发现是经常遇到的处理要求。Opencv中的直线检测是基于霍夫变换完成的。

//标准霍夫直线检测
void cv::HoughLines(
    InputArray image
    OutputArray lines
    double rho    //距离步长d=1,指该直线到原点的距离,对于屏幕坐标,原点是左上角的点
    double theta    //角度步长
    int threshold    //
    double srn =0
    double stn=0
    double min_theta=0
    double max_theta=CV_PI
)
//概率霍夫直线检测
void cv::HoughLinesP(
    InputArray image
    OutputArray lines
    double rho
    double theta
    int threshold
    double minLineLength=0
    double maxLineGap=0   
)
//霍夫圆检测
void cv::HoughCircle(
    InputArray image
    OutputArray circles//输出圆心与直径
    int method     //方法,当前只支持基于梯度的方法
    double dp    //关键参数,累加分辨率
    double minDist //两个圆之间的最小距离
    double param1=100 //边缘检测中的高梯度阈值
    double param2=100 //边缘检测中的低梯度阈值
    int minRadius=0 //最小圆半径
    int maxRadius=0 //最大圆半径
)


示例代码:

void BinaryAnalysis::hough_line_demo(Mat &image) {
	Mat edges;
	Canny(image, edges, 50, 200, 3);
	vector<Vec2f> lines;
	HoughLines(edges, lines, 1, CV_PI / 180, 150, 0, 0);
	Mat result1, result2;
	cvtColor(edges, result1, COLOR_GRAY2BGR);
	result2 = result1.clone();
	for (size_t i = 0; i < lines.size(); i++)
	{
		float rho = lines[i][0], theta = lines[i][1];
		Point pt1, pt2;
		double a = cos(theta), b = sin(theta);
		double x0 = a*rho, y0 = b*rho;
		pt1.x = cvRound(x0 + 1000 * (-b));
		pt1.y = cvRound(y0 + 1000 * (a));
		pt2.x = cvRound(x0 - 1000 * (-b));
		pt2.y = cvRound(y0 - 1000 * (a));
		line(result1, pt1, pt2, Scalar(0, 0, 255), 3, LINE_AA);
	}
	imshow("标准霍夫直线检测", result1);

	// 概率霍夫直线检测
	vector<Vec4i> linesP;
	HoughLinesP(edges, linesP, 1, CV_PI / 180, 50, 50, 10);
	for (size_t t = 0; t < linesP.size(); t++) {
		Point p1 = Point(linesP[t][0], linesP[t][1]);
		Point p2 = Point(linesP[t][2], linesP[t][3]);
		line(result2, p1, p2, Scalar(0, 0, 255), 2, 8, 0);
	}
	imshow("概率霍夫直线检测", result2);
}

void BinaryAnalysis::hough_circle_demo(Mat &image) {
	Mat gray;
	cvtColor(image, gray, COLOR_BGR2GRAY);
	GaussianBlur(gray, gray, Size(5, 5), 0, 0);
	std::vector<Vec3f> circles;
	HoughCircles(gray, circles, HOUGH_GRADIENT_ALT, 2, 10, 100, 50, 20, 40);
	for (size_t t = 0; t < circles.size(); t++) {
		Vec3f c = circles[t];
		Point center = Point(c[0], c[1]);
		int radius = c[2];
		circle(image, center, radius, Scalar(255, 0, 255), 2, 8, 0); 
		circle(image, center, 3, Scalar(255, 0, 0), 3, 8, 0);
	}
	imshow("霍夫圆检测", image);
}

最大内接圆与最小外接圆

opencv未提供API函数来寻找最大内接圆,但是可以通过点多边形测试函数巧妙地获取轮廓最大内接圆地半径,从而找到最大内接圆。

//最小外接圆
void cv::minEnclosingCircle(
    InputArray pooints
    Point2f & center
    float & radius
)
//点多边形测试函数
double cv::pointPolygonTest(
    InputArray contour
    Point2f pt
    bool measureDist //是否测量距离,当measureDist设置为true时,返回的是该点到轮廓的真是距离。设置为false时,返回的是+1、0、-1
)
//示例代码
void BinaryAnalysis::inner_extenerl_circle_demo(Mat &image) {
	Mat gray;
	cvtColor(image, gray, COLOR_BGR2GRAY);
	std::vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(gray, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
	for (size_t t = 0; t < contours.size(); t++) {
		// 最小外接圆
		Point2f pt;
		float radius;
		minEnclosingCircle(contours[t], pt, radius);
		circle(image, pt, radius, Scalar(255, 0, 0), 2, 8, 0);

		// 点多边形测试
		Mat raw_dist(image.size(), CV_32F);
		for (int i = 0; i < image.rows; i++)
		{
			for (int j = 0; j < image.cols; j++)
			{
				raw_dist.at<float>(i, j) = (float)pointPolygonTest(contours[t], Point2f((float)j, (float)i), true);
			}
		}

		// 获取最大内接圆半径
		double minVal, maxVal;
		Point maxDistPt; // inscribed circle center
		minMaxLoc(raw_dist, &minVal, &maxVal, NULL, &maxDistPt);
		minVal = abs(minVal);
		maxVal = abs(maxVal);
		circle(image, maxDistPt, maxVal, Scalar(0, 0, 255), 2, 8, 0);
	}
	imshow("最大内接圆与最小外接圆演示", image);
}

轮廓匹配

二值图像轮廓发现可以得到每个圆形的轮廓,然后计算轮廓几何矩,再根据几何矩计算图像的中心位置。根据中心位置计算中心距,然后根据中心距计算中心归一化距。再根据归一化距计算胡距,最后比较轮廓胡距之间的相似性,从而实现轮廓匹配。

//几何矩、中心距、中心归一化距可通过moments函数一次就计算出来
Moments cv::moments(
    InoutArray array
    bool binaryImage=false //是否为二值图像
)

void cv::HuMoments(
    const Moments & moments
    double hu[7] //胡距的7个值
)
//将胡距作为输入,对轮廓进行匹配。胡距具有缩放不变性与旋转不变性,所以进行轮廓外形匹配时,可以匹配到旋转与分辨率不一样的同一个轮廓。
//轮廓匹配函数
double cv::matchShapes(
    InputArray contour1
    InputArray contour2
    int method //比较方法
    double parameter //opencv3.x以后不需要
)
//示例代码
void BinaryAnalysis::contour_match_demo(Mat &image) {
	Mat src = imread("D:/images/abc.png");
	imshow("input", src);
	Mat src2 = imread("D:/images/a5.png");
	namedWindow("input2", WINDOW_FREERATIO);
	imshow("input2", src2);

	// 轮廓提取
	vector<vector<Point>> contours1;
	vector<vector<Point>> contours2;
	contours_info(src, contours1);
	contours_info(src2, contours2);
	// hu矩计算
	Moments mm2 = moments(contours2[0]);
	Mat hu2;
	HuMoments(mm2, hu2);
	// 轮廓匹配
	for (size_t t = 0; t < contours1.size(); t++) {
		Moments mm = moments(contours1[t]);
		Mat hum;
		HuMoments(mm, hum);
		double dist = matchShapes(hum, hu2, CONTOURS_MATCH_I1, 0);
		printf("contour match distance : %.2f\n", dist);
		if (dist < 1) {
			printf("draw it \n");
			Rect box = boundingRect(contours1[t]);
			rectangle(src, box, Scalar(0, 0, 255), 2, 8, 0);
		}
	}
	imshow("match result", src);
}

最大轮廓与关键点编码

根据二值图像实现轮廓发现,分析每个轮廓并根据面积找到最大轮廓,对最大轮廓使用轮廓逼近,从而得到轮廓编码点。最后绘制编码点信息并显示。

void BinaryAnalysis::max_contour_demo(Mat &image) {
	// 二值图像
	Mat mask;
	inRange(image, Scalar(0, 0, 0), Scalar(110, 110, 110), mask);
	bitwise_not(mask, mask);

	// 轮廓发现
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(mask, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	int height = image.rows;
	int width = image.cols;
	int index = -1;
	int max = 0;

	// 最大轮廓寻找
	for (size_t t = 0; t < contours.size(); t++) {
		double area = contourArea(contours[t]);
		if (area > max) {
			max = area;
			index = t;
		}
	}
	Mat result = Mat::zeros(image.size(), image.type());
	Mat pts;
	drawContours(result, contours, index, Scalar(0, 0, 255), 1, 8);

	// 关键点编码提取与绘制
	approxPolyDP(contours[index], pts, 4, true);
	for (int i = 0; i < pts.rows; i++) {
		Vec2i pt = pts.at<Vec2i>(i, 0);
		circle(result, Point(pt[0], pt[1]), 2, Scalar(0, 255, 0), 2, 8, 0);
		circle(result, Point(pt[0], pt[1]), 2, Scalar(0, 255, 0), 2, 8, 0);
	}
	imshow("最大轮廓与关键点编码", result);
}

凸包检测

//轮廓提取凸包
void cv::convexHull(
    InputArray points
    OutputArray hull
    bool clockwise = false
    bool returnPoints =true//是否返回点集  
)
//判断轮廓是否为凸包
bool cv::isContourConvex(
    InputArray contour
)

//示例代码
void BinaryAnalysis::convex_demo(Mat &image) {
	vector<vector<Point>> contours;
	contours_info(image, contours);
	for (size_t t = 0; t < contours.size(); t++) {
		vector<Point> hull;
		convexHull(contours[t], hull);
		bool isHull = isContourConvex(contours[t]);
		printf("test convex of the contours %s \n", isHull ? "Y" : "N");
		int len = hull.size();
		for (int i = 0; i < hull.size(); i++) {
			circle(image, hull[i], 4, Scalar(255, 0, 0), 2, 8, 0);
			line(image, hull[i%len], hull[(i + 1) % len], Scalar(0, 0, 255), 2, 8, 0);
		}
	}
	imshow("凸包检测", image);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据分析】numpy基础第三天

前言 本文只会讲解最常用的加、减、乘、除&#xff0c;点乘&#xff08;或叫矩阵乘法&#xff09;、还有广播机制。 本文代码 链接提取码&#xff1a;1024 第1部分&#xff1a;基础数学计算 使用NumPy进行基本的数学运算是十分直观和简单的。下面我们将展示一些基本的加、…

幻兽帕鲁服务器怎么收费?4核16G配置

幻兽帕鲁服务器价格多少钱&#xff1f;4核16G服务器Palworld官方推荐配置&#xff0c;阿里云4核16G服务器32元1个月、96元3个月&#xff0c;腾讯云换手帕服务器服务器4核16G14M带宽66元一个月、277元3个月&#xff0c;8核32G22M配置115元1个月、345元3个月&#xff0c;16核64G3…

C# 实现 gRPC 服务和调用

写在前面 gRPC 是一种与语言无关的高性能远程过程调用 (RPC) 框架。 主要优点如下&#xff1a; 1.高性能轻量化。 2.协议优先的 API 定义模式&#xff0c;默认使用协议缓冲区&#xff0c;允许与语言无关的实现。 3.可用于多种语言的工具&#xff0c;以生成强类型服务器和客户…

flutter 搜索框实现,键盘搜索按钮,清空,防抖

import package:flutter/material.dart; import package:flutter_screenutil/flutter_screenutil.dart; import package:flutter_svg/svg.dart; import package:sy_project/config/app_colors.dart; import package:sy_project/core/assets.dart;/// 搜索textview class Custom…

cmake-find_package链接第三方库

文章目录 基本调用形式和模块模式使用方式 之前我们是使用了绝对路径来链接OpenCV第三方库&#xff0c;但是现在很多库一般会自己写一些cmake文件提供给用户&#xff0c;用户可以直接使用其中的内置变量即可。使用的命令就是find_package。 基本调用形式和模块模式 find_packa…

类和对象(1)

引入 我们在c语言阶段使用的struct其实与类很相似。所以c兼容c语言结构体struct的用法&#xff0c;同时升级成了类。但为了区分&#xff0c;我们用class来定义类&#xff0c;但是结果提也是可以的。 class 类名 {//private: //public: 访问限定符 //protected://成员函…

AI开启手机摄影新时代:三星Galaxy S24 Ultra影像解读

在全球科技领域&#xff0c;生成式AI无疑是当前最为炙手可热的亮点&#xff0c;不少行业专家和业界领袖都纷纷预言&#xff0c;生成式AI技术必将重塑千行百业。 那么是否有人想过&#xff0c;如果生成式AI技术被应用在智能手机上&#xff0c;又会带来怎样翻天覆地的变革&#x…

【electron】安装网络问题处理

目录 场景排查问题排查结论electron 安装失败解决方案 新的问题electron-builder 打包失败处理 场景 在mac上使用electron进行代码开发的时候&#xff0c;无法正常下载与electron、electron-builder相关的依赖 排查问题 是不是因为没有翻墙导致资源无法下载是不是没有设置正…

【揭秘】RecursiveTask全面解析

内容概要 RecursiveTask的优点在于能够将复杂任务递归分解为更小的子任务&#xff0c;从而提高处理效率&#xff0c;通过ForkJoinPool执行&#xff0c;RecursiveTask能充分利用多核处理器资源&#xff0c;实现任务的并行化处理&#xff0c;大大加快了计算速度&#xff0c;此外…

数据结构-顺序表的实现 [王道]

本博客记录个人寒假学习内容。此篇博客内容为 顺序表的定义。 博客中截图来自王道数据结构公开课 目录 顺序表的定义 顺序表的特点 顺序表的实现--静态分配 顺序表的实现--动态分配 顺序表的定义--知识结构框架 顺序表的定义 >线性表是具有相同(每个数据元素所占的空间…

win11设置mysql开机自启

目录 命令式 1、打开命令提示符或 PowerShell&#xff1a; 2、使用管理员权限运行命令行工具&#xff1a; 3、设置 MySQL 服务为开机自启动&#xff1a; 4、启动 MySQL 服务&#xff1a; 5、 验证设置是否生效&#xff1a; 操作视图式 1、右击任务栏 ---> 选择任务管…

南京观海微电子---如何减少时序报告中的逻辑延迟

1. 引言 在FPGA逻辑电路设计中&#xff0c;FPGA设计能达到的最高性能往往由以下因素决定&#xff1a; ▪ 工作时钟偏移和时钟不确定性&#xff1b; ▪ 逻辑延迟&#xff1a;在一个时钟周期内信号经过的逻辑量&#xff1b; ▪ 网络或路径延迟&#xff1a;Vivado布局布线后引…

服务攻防-开发框架安全SpringBootStruts2LaravelThinkPHPCVE复现

知识点&#xff1a; 1、PHP-框架安全-Thinkphp&Laravel 2、J2EE-框架安全-SpringBoot&Struts2 章节点&#xff1a; 1、目标判断-端口扫描&组合判断&信息来源 2、安全问题-配置不当&CVE漏洞&弱口令爆破 3、复现对象-数据库&中间件&开发框架&am…

深入理解TCP网络协议(1)

目录 1.TCP协议的段格式 2.TCP原理 2.1确认应答 2.2超时重传 3.三次握手(重点) 4.四次挥手 1.TCP协议的段格式 我们先来观察一下TCP协议的段格式图解: 源/目的端口号:标识数据从哪个进程来,到哪个进程去 32位序号/32位确认号:TCP会话的每一端都包含一个32位&#xff08…

力扣hot100 每日温度 单调递减栈

Problem: 739. 每日温度 文章目录 思路复杂度&#x1f49d; 单调栈 思路 &#x1f469;‍&#x1f3eb; 参考题解 复杂度 ⏰ 时间复杂度: O ( n ) O(n) O(n) &#x1f30e; 空间复杂度: O ( n ) O(n) O(n) &#x1f49d; 单调栈 class Solution {public int[] dailyTem…

【人工智能】反向传播算法及梯度下降法

反向传播算法 反向传播算法英文简称为BP&#xff0c;其基本思想是逐一地由样本集中的样本计算出实际输出和误差测度&#xff0c;通过误差测度对权重序列进行调整&#xff0c;重复这个循环&#xff0c;直到误差降至最低。 步骤&#xff1a;用输出层的误差调整输出层权值矩阵&am…

阿里云1分钟成功搭建幻兽帕鲁服务器,Palworld开黑不卡

如何自建幻兽帕鲁服务器&#xff1f;基于阿里云服务器搭建幻兽帕鲁palworld服务器教程来了&#xff0c;一看就懂系列。本文是利用OOS中幻兽帕鲁扩展程序来一键部署幻兽帕鲁服务器&#xff0c;阿里云百科aliyunbaike.com分享官方基于阿里云服务器快速创建幻兽帕鲁服务器教程&…

C++11(中):智能指针

智能指针 1.内存泄漏1.1内存泄漏的概念以及危害1.2内存泄漏的场景1.3如何避免内存泄漏 2.智能指针的使用及原理2.1RAII2.2智能指针的原理2.3 std::auto_ptr2.4 定制删除器2.5 std::unique_ptr2.6 std::shared_ptr2.7 std::weak_ptr2.7.1 std::shared_ptr的循环引用2.7.2 循环引…

go语言(二十一)---- channel的关闭

channel不像文件一样需要经常去关闭&#xff0c;只有当你确实没有任何发送数据了&#xff0c;或者你想显示的结束range循环之类的&#xff0c;才去关闭channel。关闭channel后&#xff0c;无法向channel再发送数据&#xff0c;&#xff08;引发pannic错误后&#xff0c;导致接收…

力扣20、有效的括号(简单)

1 题目描述 图1 题目描述 2 题目解读 给定的字符串只包含括号&#xff0c;判断这个字符串中的括号是否按照正确顺序出现&#xff0c;即这个字符串是否有效。 3 解法一&#xff1a;栈 C的STL中的stack&#xff0c;在解题时非常好用。 3.1 解题思路 使用栈stk&#xff0c;并枚举…