两百行C++代码实现yolov5车辆计数部署(通俗易懂版)

news2024/12/23 5:33:44

这周用opencv简单实现了一下基于yolov5检测器的车辆计数功能,方法是撞线计数,代码很简单一共就两百多行,测试视频是在b站随便下载的。注:该代码只能演示视频demo效果,一些功能未完善,离实际工程应用还有距离。
实现流程:
(1)训练yolov5模型,这里就没有自己训练了,直接使用官方的开源模型yolov5s.pt;
(2)运行yolov5工程下面的export.py,将pt模型转成onnx模型;
(3)编写yolov5部署的C++工程,包括前处理、推理和后处理部分;
(4)读取视频第一帧,用yolov5检测第一帧图像的车辆目标,计算这些检测框的中心点,
(5)读取视频的后续帧,用yolov5检测每帧图像上的车辆目标,计算新目标和上一帧图像中检测框中心点的距离矩阵;
(6)通过距离约束搜索距离矩阵,确定新旧目标检测框之间的对应关系;
(7)计算对应新旧目标检测框中心点之间的连线,判断和事先设置的虚拟撞线是否相交,若相交则计数加1;
(8)重复(5)-(7)。
实际实现的时候采取的是隔帧判断而不是使用相邻帧,v1的代码实现如下:

#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>


// 常量
const float INPUT_WIDTH = 640.0;
const float INPUT_HEIGHT = 640.0;
const float SCORE_THRESHOLD = 0.5;
const float NMS_THRESHOLD = 0.45;
const float CONFIDENCE_THRESHOLD = 0.45;

const std::vector<std::string> class_name = {
"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light",
"fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
"elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee",
"skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard",
"tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch",
"potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone",
"microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear",
"hair drier", "toothbrush" };


// 画框函数
void draw_label(cv::Mat& input_image, std::string label, int left, int top)
{
	int baseLine;
	cv::Size label_size = cv::getTextSize(label, 0.7, 0.7, 1, &baseLine);
	top = std::max(top, label_size.height);
	cv::Point tlc = cv::Point(left, top);
	cv::Point brc = cv::Point(left , top + label_size.height + baseLine);
	cv::putText(input_image, label, cv::Point(left, top + label_size.height), cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(0, 255, 255), 1);
}


// 预处理
std::vector<cv::Mat> preprocess(cv::Mat& input_image, cv::dnn::Net& net)
{
	cv::Mat blob;
	cv::dnn::blobFromImage(input_image, blob, 1. / 255., cv::Size(INPUT_WIDTH, INPUT_HEIGHT), cv::Scalar(), true, false);

	net.setInput(blob);

	std::vector<cv::Mat> preprcess_image;
	net.forward(preprcess_image, net.getUnconnectedOutLayersNames());

	return preprcess_image;
}


// 后处理
std::vector<cv::Rect> postprocess(std::vector<cv::Mat>& preprcess_image, cv::Mat& output_image)
{
	std::vector<int> class_ids;
	std::vector<float> confidences;
	std::vector<cv::Rect> boxes;
	std::vector<cv::Rect> boxes_nms;

	float x_factor = output_image.cols / INPUT_WIDTH;
	float y_factor = output_image.rows / INPUT_HEIGHT;

	float* data = (float*)preprcess_image[0].data;

	const int dimensions = 85;
	const int rows = 25200;
	for (int i = 0; i < rows; ++i)
	{
		float confidence = data[4];
		if (confidence >= CONFIDENCE_THRESHOLD)
		{
			float* classes_scores = data + 5;
			cv::Mat scores(1, class_name.size(), CV_32FC1, classes_scores);
			cv::Point class_id;
			double max_class_score;
			cv::minMaxLoc(scores, 0, &max_class_score, 0, &class_id);
			if (max_class_score > SCORE_THRESHOLD)
			{
				confidences.push_back(confidence);
				class_ids.push_back(class_id.x);

				float cx = data[0];
				float cy = data[1];
				float w = data[2];
				float h = data[3];
				int left = int((cx - 0.5 * w) * x_factor);
				int top = int((cy - 0.5 * h) * y_factor);
				int width = int(w * x_factor);
				int height = int(h * y_factor);
				boxes.push_back(cv::Rect(left, top, width, height));
			}
		}
		data += 85;
	}

	std::vector<int> indices;
	cv::dnn::NMSBoxes(boxes, confidences, SCORE_THRESHOLD, NMS_THRESHOLD, indices);
	for (size_t i = 0; i < indices.size(); i++)
	{
		int idx = indices[i];
		cv::Rect box = boxes[idx];
		boxes_nms.push_back(box);

		int left = box.x;
		int top = box.y;
		int width = box.width;
		int height = box.height;
		cv::rectangle(output_image, cv::Point(left, top), cv::Point(left + width, top + height), cv::Scalar(255, 0, 0), 1);

		std::string label = cv::format("%.2f", confidences[idx]);
		label = class_name[class_ids[idx]] + ":" + label;
		draw_label(output_image, label, left, top);
	}
	return boxes_nms;
}


std::vector<cv::Point> get_center(std::vector<cv::Rect> detections)
{
	std::vector<cv::Point> detections_center(detections.size());
	for (size_t i = 0; i < detections.size(); i++)
	{
		detections_center[i] = cv::Point(detections[i].x + detections[i].width / 2, detections[i].y + detections[i].height / 2);
	}

	return detections_center;
}


float get_distance(cv::Point p1, cv::Point p2)
{
	return sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));
}


bool is_cross(cv::Point p1, cv::Point p2)
{
	if (p1.x == p2.x) return false;

	int y = 500;  //line1: y = 500
	float k = (p1.y - p2.y) / (p1.x - p2.x);  //
	float b = p1.y - k * p1.x; //line2: y = kx + b
	float x = (y - b) / k;
	return (x > std::min(p1.x, p2.x) && x < std::max(p1.x, p2.x));
}


int main(int argc, char** argv)
{
	cv::VideoCapture capture("test.mp4");
	cv::Mat frame;
	cv::dnn::Net net = cv::dnn::readNet("yolov5s-f32.onnx");

	int frame_num = 0;
	int count = 0;
	std::vector<cv::Point> detections_center_old;
	std::vector<cv::Point> detections_center_new;

	while(cv::waitKey(1) < 0)
	{
	    capture >> frame;
		if (frame.empty())
			break;

		std::cout << "******************************************************************* frame_num: " << frame_num << std::endl;

		cv::Mat image = frame.clone();
		std::vector<cv::Mat> preprcess_image = preprocess(image, net);

		std::vector<cv::Rect> detections = postprocess(preprcess_image, image);

		if (frame_num == 0)
		{
			detections_center_old = get_center(detections);

			std::cout << "detections_center:" << std::endl;
			for (size_t i = 0; i < detections_center_old.size(); i++)
			{
				std::cout << detections_center_old[i] << std::endl;
			}
		}
		else if (frame_num % 2 == 0)
		{
			detections_center_new = get_center(detections);

			std::cout << "detections_center:" << std::endl;
			for (size_t i = 0; i < detections_center_new.size(); i++)
			{
				std::cout << detections_center_new[i] << std::endl;
			}

			std::vector<std::vector<float>> distance_matrix(detections_center_new.size(), std::vector<float>(detections_center_old.size()));
			std::cout << "distance_matrix:" << std::endl;
			for (size_t i = 0; i < detections_center_new.size(); i++)
			{
				for (size_t j = 0; j < detections_center_old.size(); j++)
				{
					distance_matrix[i][j] = get_distance(detections_center_new[i], detections_center_old[j]); //
					std::cout << distance_matrix[i][j] << " ";
				}
				std::cout << std::endl;
			}

			std::cout << "min_index:" << std::endl;
			std::vector<float> min_indices(detections_center_new.size());
			for (size_t i = 0; i < detections_center_new.size(); i++)
			{
				std::vector<float> distance_vector = distance_matrix[i];
				int min_index = std::min_element(distance_vector.begin(), distance_vector.end()) - distance_vector.begin();
				min_indices[i] = min_index;
				std::cout << min_index << " ";
			}
			std::cout << std::endl;

			for (size_t i = 0; i < detections_center_new.size(); i++)
			{
				cv::Point p1 = detections_center_new[i];
				cv::Point p2 = detections_center_old[min_indices[i]];
				std::cout << p1 << " " << p2 << std::endl;

				if (is_cross(p1, p2))
				{
					std::cout << "is_cross" << p1 << " " << p2 << std::endl;
					count++;
				}
			}
			detections_center_old = detections_center_new;
		}

		frame_num++;

		cv::putText(image, "car num: " + std::to_string(count), cv::Point(20, 50), cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(0, 255, 255), 1);
		cv::line(image, cv::Point(0, 500), cv::Point(1280, 500) , cv::Scalar(0, 0, 255));
		cv::imshow("output", image);
		cv::imwrite(std::to_string(frame_num) + ".jpg", image);
	}

	capture.release();
	return 0;
}

在调试中,发现v1的实现存在如下问题:出现新目标的时候,计算新旧检测框的对应关系出现匹配错误,导致计数偏多。因此在v2中设置匹配的距离阈值,并简化了判断检测框中心点连线和撞线是否相交的方法。
v2的代码实现如下:

#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>


#define DEBUG


// 常量
const float INPUT_WIDTH = 640.0;
const float INPUT_HEIGHT = 640.0;
const float SCORE_THRESHOLD = 0.5;
const float NMS_THRESHOLD = 0.25;
const float CONFIDENCE_THRESHOLD = 0.5;

const std::vector<std::string> class_name = {
	"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light",
	"fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
	"elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee",
	"skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard",
	"tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
	"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch",
	"potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone",
	"microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear",
	"hair drier", "toothbrush" };

const int IMAGE_WIDTH = 1280;
const int IMAGE_HEIGHT = 720;
const int LINE_HEIGHT = IMAGE_HEIGHT / 2;


//画出检测框和标签
void draw_label(cv::Mat& input_image, std::string label, int left, int top)
{
	int baseLine;
	cv::Size label_size = cv::getTextSize(label, 0.7, 0.7, 1, &baseLine);
	top = std::max(top, label_size.height);
	cv::Point tlc = cv::Point(left, top);
	cv::Point brc = cv::Point(left , top + label_size.height + baseLine);
	cv::putText(input_image, label, cv::Point(left, top + label_size.height), cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(0, 255, 255), 1);
}


//预处理
std::vector<cv::Mat> preprocess(cv::Mat& input_image, cv::dnn::Net& net)
{
	cv::Mat blob;
	cv::dnn::blobFromImage(input_image, blob, 1. / 255., cv::Size(INPUT_WIDTH, INPUT_HEIGHT), cv::Scalar(), true, false);

	net.setInput(blob);

	std::vector<cv::Mat> preprcess_image;
	net.forward(preprcess_image, net.getUnconnectedOutLayersNames());

	return preprcess_image;
}


//后处理
std::vector<cv::Rect> postprocess(std::vector<cv::Mat>& preprcess_image, cv::Mat& output_image)
{
	std::vector<int> class_ids;
	std::vector<float> confidences;
	std::vector<cv::Rect> boxes;
	std::vector<cv::Rect> boxes_nms;

	float x_factor = output_image.cols / INPUT_WIDTH;
	float y_factor = output_image.rows / INPUT_HEIGHT;

	float* data = (float*)preprcess_image[0].data;

	const int dimensions = 85;
	const int rows = 25200;
	for (int i = 0; i < rows; ++i)
	{
		float confidence = data[4];
		if (confidence >= CONFIDENCE_THRESHOLD)
		{
			float* classes_scores = data + 5;
			cv::Mat scores(1, class_name.size(), CV_32FC1, classes_scores);
			cv::Point class_id;
			double max_class_score;
			cv::minMaxLoc(scores, 0, &max_class_score, 0, &class_id);
			if (max_class_score > SCORE_THRESHOLD)
			{
				confidences.push_back(confidence);
				class_ids.push_back(class_id.x);

				float cx = data[0];
				float cy = data[1];
				float w = data[2];
				float h = data[3];
				int left = int((cx - 0.5 * w) * x_factor);
				int top = int((cy - 0.5 * h) * y_factor);
				int width = int(w * x_factor);
				int height = int(h * y_factor);
				boxes.push_back(cv::Rect(left, top, width, height));
			}
		}
		data += 85;
	}

	std::vector<int> indices;
	cv::dnn::NMSBoxes(boxes, confidences, SCORE_THRESHOLD, NMS_THRESHOLD, indices);
	for (size_t i = 0; i < indices.size(); i++)
	{
		int idx = indices[i];
		cv::Rect box = boxes[idx];
		boxes_nms.push_back(box);

		int left = box.x;
		int top = box.y;
		int width = box.width;
		int height = box.height;
		cv::rectangle(output_image, cv::Point(left, top), cv::Point(left + width, top + height), cv::Scalar(255, 0, 0), 1);

		std::string label = cv::format("%.2f", confidences[idx]);
		//label = class_name[class_ids[idx]] + ":" + label;
		label = "car";
		draw_label(output_image, label, left, top);
	}

	return boxes_nms;
}


//计算检测框的中心
std::vector<cv::Point> get_center(std::vector<cv::Rect> detections)
{
	std::vector<cv::Point> detections_center(detections.size());
	for (size_t i = 0; i < detections.size(); i++)
	{
		detections_center[i] = cv::Point(detections[i].x + detections[i].width / 2, detections[i].y + detections[i].height / 2);
	}

	return detections_center;
}


//计算两点间距离
float get_distance(cv::Point p1, cv::Point p2)
{
	return sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));
}


//判断连接相邻两帧对应检测框中心的线段是否与红线相交
bool is_cross(cv::Point p1, cv::Point p2)
{
	return (p1.y <= LINE_HEIGHT && p2.y > LINE_HEIGHT) || (p1.y > LINE_HEIGHT && p2.y <= LINE_HEIGHT);
}


int main(int argc, char** argv)
{
	cv::VideoCapture capture("test.mp4");
	cv::Mat frame;
	cv::dnn::Net net = cv::dnn::readNet("yolov5s-f32.onnx");

	int frame_num = 0;
	int count = 0;
	std::vector<cv::Point> detections_center_old;
	std::vector<cv::Point> detections_center_new;

	while(cv::waitKey(1) < 0)
	{
	    capture >> frame;
		if (frame.empty())
			break;

		std::cout << "******************************************************************* frame_num: " << frame_num << std::endl;

		cv::Mat image = frame.clone();
		std::vector<cv::Mat> preprcess_image = preprocess(image, net);

		std::vector<cv::Rect> detections = postprocess(preprcess_image, image);

		if (frame_num == 0)
		{
			detections_center_old = get_center(detections);

#ifdef DEBUG
			std::cout << "detections_center:" << std::endl;
			for (size_t i = 0; i < detections_center_old.size(); i++)
			{
				std::cout << detections_center_old[i] << std::endl;
			}
#endif // DEBUG
		}
		else if (frame_num % 2 == 0)
		{
			detections_center_new = get_center(detections);

#ifdef DEBUG
			std::cout << "detections_center:" << std::endl;
			for (size_t i = 0; i < detections_center_new.size(); i++)
			{
				std::cout << detections_center_new[i] << std::endl;
			}
#endif // DEBUG

			std::vector<std::vector<float>> distance_matrix(detections_center_new.size(), std::vector<float>(detections_center_old.size())); //距离矩阵
			for (size_t i = 0; i < detections_center_new.size(); i++)
			{
				for (size_t j = 0; j < detections_center_old.size(); j++)
				{
					distance_matrix[i][j] = get_distance(detections_center_new[i], detections_center_old[j]); 
				}
			}

#ifdef DEBUG
			std::cout << "min_index:" << std::endl;
#endif // DEBUG

			std::vector<float> min_indices(detections_center_new.size());
			for (size_t i = 0; i < detections_center_new.size(); i++)
			{
				std::vector<float> distance_vector = distance_matrix[i];
				float min_val = *std::min_element(distance_vector.begin(), distance_vector.end());
				int min_index = -1;
				if (min_val < LINE_HEIGHT / 5)
					 min_index = std::min_element(distance_vector.begin(), distance_vector.end()) - distance_vector.begin();
				
				min_indices[i] = min_index;
#ifdef DEBUG
				std::cout << min_index << " ";
#endif // DEBUG
			}
			std::cout << std::endl;

			for (size_t i = 0; i < detections_center_new.size(); i++)
			{
				if (min_indices[i] < 0)
					continue;

				cv::Point p1 = detections_center_new[i];
				cv::Point p2 = detections_center_old[min_indices[i]];

#ifdef DEBUG
				std::cout << p1 << " " << p2 << std::endl;
#endif // DEBUG

				if (is_cross(p1, p2))
				{
#ifdef DEBUG
					std::cout << "is_cross" << p1 << " " << p2 << std::endl;
#endif // DEBUG
					count++;
				}
			}

			detections_center_old = detections_center_new;
		}

		cv::putText(image, "car num: " + std::to_string(count), cv::Point(20, 50), cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(0, 0, 255), 1);
		cv::line(image, cv::Point(0, LINE_HEIGHT), cv::Point(IMAGE_WIDTH, LINE_HEIGHT), cv::Scalar(0, 0, 255));
		cv::imshow("output", image);

#ifdef DEBUG
		if (frame_num % 2 == 0)
			cv::imwrite(std::to_string(frame_num) + ".jpg", image);
#endif // DEBUG

		frame_num++;
	}

	capture.release();
	return 0;
}

检测效果实现如下,效果还是可以的。完整视频中有一次计数异常,是因为检测器不准导致车辆检测框位置漂移,可以后续优化。注:由于官方提供的coco80类的开源权重文件用于车辆检测效果不是很好,LZ把检测出的类别直接固定为car,实际应自己重新训练一个车辆检测的模型。
在这里插入图片描述

代码、测试视频和转好的权重文件放在下载链接:点击跳转

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

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

相关文章

JMeter整体综述

JMeter整体综述1. JMeter体系结构及运行原理1.1 主要的组件1.2 运行原理2. 元件执行顺序和作用域2.1 元件执行顺序2.2 元件执行作用域3. 参考1. JMeter体系结构及运行原理 负载模拟&#xff1a;负责模拟用户请求。如取样器有参数化的需求&#xff0c;可通过配置元件或前置处理器…

15.JavaScript 01

文章目录一、概念二、功能三、 JavaScript发展史四、 ECMAScript&#xff1a;客户端脚本语言的标准1、基本语法1. 与html结合方式2. 注释3. 数据类型4. 变量5. 运算符6. 流程控制语句7. JS特殊语法8. 练习&#xff1a;99乘法表2、基本对象1. Function&#xff1a;函数(方法)对象…

上线3天,下载4万,ChatGPT 中文版VSCode插件来了

ChatGPT 的 Debug 功能&#xff0c;有人应用化了。 ChatGPT 这几天可谓是风头无两。作为一个问答语言模型&#xff0c;它最大的优点就是可以回答与编程相关的问题&#xff0c;甚至回复一段代码。 尽管有人指出 ChatGPT 生成的代码有错误&#xff0c;但程序员们还是对它写代码、…

图像配准开源数据集资源汇总

Brown 数据集 数据集下载链接&#xff1a;http://suo.nz/3042bh 数据集由 1024 x 1024 位图 (.bmp) 图像组成&#xff0c;每个图像包含一个 16 x 16 图像块阵列。每个补丁都被采样为 64 x 64 灰度&#xff0c;具有规范的比例和方向。 ETHZ Toys 数据集下载链接&#xff1a…

Java中的语法糖(真甜)

什么是语法糖&#xff08;Syntactic sugar&#xff09; 语法糖是一个计算机数据&#xff0c;特指在编程语言中添加的某种语法&#xff0c;这种语法对语言的功能没有影响&#xff0c;但是更方便程序员使用。语法糖让程序更加简洁&#xff0c;有更高的可读性。 糖嘛&#xff0c…

08.DashBoard流监控配置

08.DashBoard流监控配置 每个服务提供者都需要实现actuator&#xff0c;才可以实现流量监控。 导入Maven依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId><…

【图像去噪】空域+频域滤波图像去噪【含GUI Matlab源码 914期】

⛄一、获取代码方式 获取代码方式1&#xff1a; 完整代码已上传我的资源&#xff1a;【图像去噪】基于matlab GUI空域频域滤波图像去噪【含Matlab源码 914期】 获取代码方式2&#xff1a; 通过订阅紫极神光博客付费专栏&#xff0c;凭支付凭证&#xff0c;私信博主&#xff0…

移动端防抓包实践

目录介绍 01.整体概述介绍 1.1 项目背景1.2 思考问题1.3 设计目标1.4 收益分析 02.市面抓包的分析 2.1 Https三要素2.2 抓包核心原理2.3 搞定CA证书2.4 突破CA证书校验2.5 如何搞定加解密2.6 Charles原理2.7 抓包原理图2.8 抓包核心流程 03.防止抓包思路 3.1 先看如何抓包3.2 …

C++计算机视觉库OpenCV在Visual Studio 2022的配置方法

本文介绍在Visual Studio 2022中配置、编译C 计算机视觉库OpenCV的方法。 1 OpenCV库配置 首先&#xff0c;我们进行OpenCV库的下载与安装。作为一个开源的库&#xff0c;我们直接在其官方下载网站&#xff08;https://opencv.org/releases/&#xff09;中进行下载即可&#x…

实现无入侵式C++代码mock工具

为了实现真正无侵入式的mock&#xff0c;我们基于开源Hook框架Frida-gum提供的API&#xff0c;利用C模板进行封装&#xff0c;作者编写了一个简单实用的mock工具&#xff0c;在此开源分享&#xff08;代码详见附录&#xff09;。背景在单元测试中&#xff0c;往往需要减少被测函…

如何实现高性能网络编程-ChatGPT怎么看

hi ,大家好&#xff0c;我是大师兄。听说最近chatgpt特别火&#xff0c;那我们邀请一下chatgpt如何实现&#xff1a;我们先来小试牛刀&#xff1a;刚开始用先用英文交流一下&#xff0c;然后试一下中文&#xff1a;元芳你怎么看&#xff1f;下期直播主题--网络编程 (如何实现高…

用 AWTK 和 AWPLC 快速开发嵌入式应用程序 (7)- 用状态机实现红绿灯

AWPLC 目前还处于开发阶段的早期&#xff0c;写这个系列文章的目的&#xff0c;除了用来验证目前所做的工作外&#xff0c;还希望得到大家的指点和反馈。如果您有任何疑问和建议&#xff0c;请在评论区留言。 1. 背景 AWTK 全称 Toolkit AnyWhere&#xff0c;是 ZLG 开发的开源…

kaggle实战:基于超市消费数据的用户个性化分析案例

大家好&#xff0c;今天给大家分享一篇 kaggle 数据集的新文章&#xff1a;基于一份超市消费数据集的用户个性化分析以及用户分群的实现。 更多详细内容参考原数据集地址&#xff1a; https://www.kaggle.com/code/sonalisingh1411/customer-personality-analysis-segmentati…

实验八 网络优化与正则化(3)不同优化算法比较

目录7.3 不同优化算法的比较分析7.3.1 优化算法的实验设定7.3.1.1 2D可视化实验7.3.1.2 简单拟合实验7.3.1.3 与Torch API对比&#xff0c;验证正确性7.3.2 学习率调整7.3.2.1 AdaGrad算法7.3.2.2 RMSprop算法7.3.3 梯度估计修正7.3.3.1 动量法7.3.3.2 Adam算法7.3.4 不同优化器…

java基于Springboot的简历系统-计算机毕业设计

项目介绍 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;简历系统当然也不能排除在外。简历系统是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;…

SAP ABAP CDS view Association 引入的缘由

ABAP CDS view 支持三种 join 方式&#xff1a; Inner JoinLeft Outer joinRight outer join 我们使用 ABAP Development Tool 的 CDS view 向导创建一个 CDS view&#xff1a; 向导里包含的 $ 和大括号就是占位符&#xff0c;需要开发人员自己指定&#xff1a; 我们把占位符…

奇舞周刊475期:2022年 CSS 生态圈技术趋势!

记得点击文章末尾的“ 阅读原文 ”查看哟~下面先一起看下本期周刊 摘要 吧~奇舞推荐■ ■ ■2022年 CSS 生态圈技术趋势&#xff01;一年一度的 State of CSS 调查结果正式公布&#xff01;通过本文看看2022年 CSS 生态圈的技术趋势&#xff01;React Streaming SSR 原理解析Re…

BI技巧丨RANKX浮点运算

RANKX这个函数&#xff0c;白茶之前已经写过很多期了&#xff0c;本期是对RANKX函数一个细节问题的补充。 我们常见的数据类型有很多&#xff0c;用来聚合的主要有三种数据类型&#xff1a;文本、整数、小数。 在大部分场合&#xff0c;小数是实际FACT数据中最为常见的数据类…

[1180]clickhouse查看数据库和表的容量大小

文章目录1.查看数据库容量、行数、压缩率2.查看数据表容量、行数、压缩率3.查看数据表分区信息4.查看数据表字段的信息5. 查看表的各个指标6.跟踪分区7.检查数据大小在mysql中information_schema这个数据库中保存了mysql服务器所有数据库的信息&#xff0c; 而在clickhouse&…

[附源码]Python计算机毕业设计SSM基于健身房管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…