yolov5目标检测多线程C++部署

news2024/9/20 20:51:02

C++多线程复习

下面的代码搭建了简单的一个生产者-消费者模型,在capture()函数中进行入队操作,infer()函数中进行出队操作,为了模拟采图-推理流程,在函数中调用Sleep()函数延时。

#include <iostream>
#include <string>
#include <queue>
#include <thread>
#include <windows.h>

std::queue<std::string> jobs;

void capture()
{
	int id = 0;
	while (true)
	{
		std::string name = std::to_string(id++) + ".jpg";
		std::cout << "capture: " << name << " jobs.size():" << jobs.size() << std::endl;
		jobs.push(name);
		Sleep(1000);
	}
}

void infer()
{
	while (true)
	{
		if (!jobs.empty())
		{
			auto pic = jobs.front();
			jobs.pop();
			std::cout <<"infer: "<< pic << std::endl;
			Sleep(1000);
		}
	}
}


int main()
{
	std::thread t0(capture);
	std::thread t1(infer);

	t0.join();
	t1.join();

	return 0;
}

输出结果:

capture: 0.jpg jobs.size():0
infer: 0.jpg
capture: 1.jpg jobs.size():0
infer: 1.jpg
capture: 2.jpg jobs.size():0
infer: 2.jpg
capture: 3.jpg jobs.size():0
infer: 3.jpg
capture: 4.jpg jobs.size():0
infer: 4.jpg
capture: 5.jpg jobs.size():0
infer: 5.jpg
capture: 6.jpg jobs.size():0
infer: 6.jpg
capture: 7.jpg jobs.size():0
infer: 7.jpg
capture: 8.jpg jobs.size():0
infer: 8.jpg
capture: 9.jpg jobs.size():0
infer: 9.jpg
capture: 10.jpg jobs.size():0
infer: 10.jpg
...

现在我们把capture函数中的Sleep(1000)改成Sleep(500),再次执行程序,则输出:

capture: 0.jpg jobs.size():0
infer: 0.jpg
capture: 1.jpg jobs.size():0
infer: 1.jpg
capture: 2.jpg jobs.size():0
capture: 3.jpg jobs.size():1
infer: 2.jpg
capture: 4.jpg jobs.size():1
capture: 5.jpg jobs.size():2
infer: 3.jpg
capture: 6.jpg jobs.size():2
capture: 7.jpg jobs.size():3
infer: 4.jpg
capture: 8.jpg jobs.size():3
capture: 9.jpg jobs.size():4
infer: 5.jpg
capture: 10.jpg jobs.size():4
...

此时发现采图-推理流程不能同步。为了解决这个问题,加入对队列长度的限制:

#include <iostream>
#include <string>
#include <queue>
#include <thread>
#include <windows.h>

std::queue<std::string> jobs;

const int limit = 3;

void capture()
{
	int id = 0;
	while (true)
	{
		std::string name = std::to_string(id++) + ".jpg";
		std::cout << "capture: " << name << " jobs.size():" << jobs.size() << std::endl;

		if(jobs.size()< limit)
			jobs.push(name);

		Sleep(500);
	}
}

void infer()
{
	while (true)
	{
		if (!jobs.empty())
		{
			auto pic = jobs.front();
			jobs.pop();
			std::cout <<"infer: "<< pic << std::endl;
			Sleep(1000);
		}
	}
}


int main()
{
	std::thread t0(capture);
	std::thread t1(infer);

	t0.join();
	t1.join();

	return 0;
}

此时输出结果:

capture: 0.jpg jobs.size():0
infer: 0.jpg
capture: 1.jpg jobs.size():0
infer: 1.jpg
capture: 2.jpg jobs.size():0
capture: 3.jpg jobs.size():1
infer: 2.jpg
capture: 4.jpg jobs.size():1
capture: 5.jpg jobs.size():2
infer: 3.jpg
capture: 6.jpg jobs.size():2
capture: 7.jpg jobs.size():3
infer: 4.jpg
capture: 8.jpg jobs.size():2
capture: 9.jpg jobs.size():3
infer: 5.jpg
capture: 10.jpg jobs.size():2
...

由于std::queue不是线程安全的数据结构,故引入锁std::mutex:

#include <iostream>
#include <string>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <windows.h>


std::queue<std::string> jobs;

std::mutex lock;


void capture()
{
	int id = 0;
	while (true)
	{
		{
			std::unique_lock<std::mutex> l(lock);
			std::string name = std::to_string(id++) + ".jpg";
			std::cout << "capture: " << name << " " << "jobs.size(): " << jobs.size() << std::endl;
		}

		Sleep(500);
	}
}

void infer()
{ 
	while (true)
	{
		if (!jobs.empty())
		{
			{
				std::lock_guard<std::mutex> l(lock);
				auto job = jobs.front();
				jobs.pop();
				std::cout << "infer: " << job << std::endl;
			}
			Sleep(1000);
		}
	}
}


int main()
{
	std::thread t0(capture);
	std::thread t1(infer);

	t0.join();
	t1.join();

	return 0;
}

此时输出:

capture: 0.jpg jobs.size(): 0
capture: 1.jpg jobs.size(): 0
capture: 2.jpg jobs.size(): 0
capture: 3.jpg jobs.size(): 0
capture: 4.jpg jobs.size(): 0
capture: 5.jpg jobs.size(): 0
capture: 6.jpg jobs.size(): 0
capture: 7.jpg jobs.size(): 0
capture: 8.jpg jobs.size(): 0
capture: 9.jpg jobs.size(): 0
capture: 10.jpg jobs.size(): 0
...

有时候生产者还需要拿到消费者处理之后的结果,因此引入std::promise和std::condition_variable对程序进行完善:

#include <iostream>
#include <string>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <windows.h>


struct Job
{
	std::string input;
	std::shared_ptr<std::promise<std::string>> pro;
};

std::queue<Job> jobs;

std::mutex lock;

std::condition_variable cv;

const int limit = 5;

void capture()
{
	int id = 0;
	while (true)
	{
		Job job;
		{
			std::unique_lock<std::mutex> l(lock);
			std::string name = std::to_string(id++) + ".jpg";
			std::cout << "capture: " << name << " " << "jobs.size(): " << qjobs.size() << std::endl;
			cv.wait(l, [&]() { return qjobs.size() < limit; });

			job.input = name;
			job.pro.reset(new std::promise<std::string>());
			jobs.push(job);
		}

		auto result = job.pro->get_future().get();
		std::cout << result << std::endl;

		Sleep(500);
	}
}

void infer()
{ 
	while (true)
	{
		if (!qjobs.empty())
		{
			{
				std::lock_guard<std::mutex> l(lock);
				auto job = jobs.front();
				jobs.pop();
				cv.notify_all();
				std::cout << "infer: " << job.input << std::endl;

				auto result = job.input + " after infer";
				job.pro->set_value(result);
			}
			Sleep(1000);
		}
	}
}


int main()
{
	std::thread t0(capture);
	std::thread t1(infer);

	t0.join();
	t1.join();

	return 0;
}

输出:

capture: 0.jpg jobs.size(): 0
infer: 0.jpg
0.jpg after infer
capture: 1.jpg jobs.size(): 0
infer: 1.jpg
1.jpg after infer
capture: 2.jpg jobs.size(): 0
infer: 2.jpg
2.jpg after infer
capture: 3.jpg jobs.size(): 0
infer: 3.jpg
3.jpg after infer
capture: 4.jpg jobs.size(): 0
infer: 4.jpg
4.jpg after infer
capture: 5.jpg jobs.size(): 0
infer: 5.jpg
5.jpg after infer
capture: 6.jpg jobs.size(): 0
infer: 6.jpg
6.jpg after infer
capture: 7.jpg jobs.size(): 0
infer: 7.jpg
7.jpg after infer
capture: 8.jpg jobs.size(): 0
infer: 8.jpg
8.jpg after infer
capture: 9.jpg jobs.size(): 0
infer: 9.jpg
9.jpg after infer
capture: 10.jpg jobs.size(): 0
infer: 10.jpg
10.jpg after infer
...

yolov5目标检测多线程C++部署

有了上面的基础,我们来写一个基本的目标检测多线程部署程序,为了简单起见选用OpenCV的dnn作为推理框架,出于篇幅限制下面只给出main.cpp部分:

#include <iostream>
#include <string>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <windows.h>

#include "yolov5.h"


struct Job
{
	cv::Mat input_image;
	std::shared_ptr<std::promise<cv::Mat>> output_image;
};

std::queue<Job> jobs;

std::mutex lock;

std::condition_variable c_v;

const int limit = 10;

void capture(cv::VideoCapture cap)
{
	while (true)
	{
		Job job;
		cv::Mat frame;
		{
			cap.read(frame);
			if (frame.empty())
				break;

			std::unique_lock<std::mutex> l(lock);
			c_v.wait(l, [&]() { return jobs.size() < limit; });

			job.input_image = frame;
			job.output_image.reset(new std::promise<cv::Mat>());
			jobs.push(job);
		}

		cv::Mat result = job.output_image->get_future().get();

		cv::imshow("result", result);
		cv::waitKey(1);
	}
}

void infer(cv::dnn::Net net)
{ 
	while (true)
	{
		if (!jobs.empty())
		{
			std::lock_guard<std::mutex> l(lock);
			auto job = jobs.front();
			jobs.pop();
			c_v.notify_all();

			cv::Mat input_image = job.input_image, blob, output_image;
			pre_process(input_image, blob);

			std::vector<cv::Mat> network_outputs;
			process(blob, net, network_outputs);

			post_process(input_image, output_image, network_outputs);

			job.output_image->set_value(output_image);
		}
	}
}


int main(int argc, char* argv[])
{
	cv::VideoCapture cap("test.mp4");

	cv::dnn::Net net = cv::dnn::readNet("yolov5n.onnx");

	std::thread t0(capture, cap);
	std::thread t1(infer, net);

	t0.join();
	t1.join();

	return 0;
}

接下来我们模拟多个模型同时推理,先给出单线程串行的程序:

#include <iostream>
#include <string>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <windows.h>

#include "yolov5.h"


int main(int argc, char* argv[])
{
	cv::VideoCapture cap("test.mp4");
	//cap.open(0);

	cv::dnn::Net net1 = cv::dnn::readNet("yolov5n.onnx");
	cv::dnn::Net net2 = cv::dnn::readNet("yolov5s.onnx");

	cv::Mat frame;
	while (true) 
	{
		clock_t start = clock();

		cap.read(frame);
		if (frame.empty())
			break;

		cv::Mat input_image = frame, blob;
		pre_process(input_image, blob);

		std::vector<cv::Mat> network_outputs1, network_outputs2;
		process(blob, net1, network_outputs1);
		process(blob, net2, network_outputs2);

		cv::Mat output_image1, output_image2;
		post_process(input_image, output_image1, network_outputs1);
		post_process(input_image, output_image2, network_outputs2);

		clock_t end = clock();
		std::cout << end - start << "ms" << std::endl;

		cv::imshow("result1", output_image1);
		cv::imshow("result2", output_image2);
		cv::waitKey(1);
	}

	return 0;
}

输出结果:

infer1+infer2:191ms
infer1+infer2:142ms
infer1+infer2:134ms
infer1+infer2:130ms
infer1+infer2:129ms
infer1+infer2:124ms
infer1+infer2:124ms
infer1+infer2:121ms
infer1+infer2:124ms
infer1+infer2:122ms
...

多线程并行的写法修改如下:

#include <iostream>
#include <string>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <windows.h>

#include "yolov5.h"


struct Job
{
	cv::Mat input_image;
	std::shared_ptr<std::promise<cv::Mat>> output_image;
};

std::queue<Job> jobs1,jobs2;

std::mutex lock1, lock2;

std::condition_variable cv1, cv2;

const int limit = 10;

void capture(cv::VideoCapture cap)
{
	while (true)
	{
		Job job1, job2;
		cv::Mat frame;

		clock_t start = clock();

		cap.read(frame);
		if (frame.empty())
			break;

		{
			std::unique_lock<std::mutex> l1(lock1);
			cv1.wait(l1, [&]() { return jobs1.size() < limit; });

			job1.input_image = frame;
			job1.output_image.reset(new std::promise<cv::Mat>());
			jobs1.push(job1);
		}

		{
			std::unique_lock<std::mutex> l2(lock2);
			cv1.wait(l2, [&]() { return jobs2.size() < limit; });

			job2.input_image = frame;
			job2.output_image.reset(new std::promise<cv::Mat>());
			jobs2.push(job2);
		}

		cv::Mat result1 = job1.output_image->get_future().get();
		cv::Mat result2 = job2.output_image->get_future().get();

		clock_t end = clock();
		std::cout <<"capture: "<< end - start << "ms" << std::endl;

		cv::imshow("result1", result1);
		cv::imshow("result2", result2);
		cv::waitKey(1);
	}
}

void infer1(cv::dnn::Net net)
{ 
	while (true)
	{
		if (!jobs1.empty())
		{
			clock_t start = clock();

			std::lock_guard<std::mutex> l1(lock1);
			auto job = jobs1.front();
			jobs1.pop();
			cv1.notify_all();

			cv::Mat input_image = job.input_image, blob, output_image;
			pre_process(input_image, blob);

			std::vector<cv::Mat> network_outputs;
			process(blob, net, network_outputs);

			post_process(input_image, output_image, network_outputs);

			job.output_image->set_value(output_image);

			clock_t end = clock();
			std::cout << "infer1: " << end - start << "ms" << std::endl;
		}
	}
}

void infer2(cv::dnn::Net net)
{
	while (true)
	{
		if (!jobs2.empty())
		{
			clock_t start = clock();

			std::lock_guard<std::mutex> l2(lock2);
			auto job = jobs2.front();
			jobs2.pop();
			cv2.notify_all();

			cv::Mat input_image = job.input_image, blob, output_image;
			pre_process(input_image, blob);

			std::vector<cv::Mat> network_outputs;
			process(blob, net, network_outputs);

			post_process(input_image, output_image, network_outputs);

			job.output_image->set_value(output_image);

			clock_t end = clock();
			std::cout << "infer2: " << end - start << "ms" << std::endl;
		}
	}
}


int main(int argc, char* argv[])
{
	cv::VideoCapture cap("test.mp4");
	//cap.open(0);

	cv::dnn::Net net1 = cv::dnn::readNet("yolov5n.onnx");
	cv::dnn::Net net2 = cv::dnn::readNet("yolov5s.onnx");

	std::thread t0(capture, cap);
	std::thread t1(infer1, net1);
	std::thread t2(infer2, net2);

	t0.join();
	t1.join();
	t2.join();

	return 0;
}

输出:

infer1: 98ms
infer2: 136mscapture: 155ms

infer1: 80ms
infer2: 110ms
capture: 113ms
infer1: 92ms
infer2: 101mscapture: 103ms

infer1: 85ms
infer2: 97ms
capture: 100ms
infer1: 85ms
infer2: 100mscapture: 102ms
...

上面的程序还有一点小问题:视频播放完时程序无法正常退出。继续修正如下:

#include <iostream>
#include <string>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <windows.h>

#include "yolov5.h"


struct Job
{
	cv::Mat input_image;
	std::shared_ptr<std::promise<cv::Mat>> output_image;
};

std::queue<Job> jobs1,jobs2;

std::mutex lock1, lock2;

std::condition_variable cv1, cv2;

const int limit = 10;

bool stop = false;

void capture(cv::VideoCapture cap)
{
	while (true)
	{
		Job job1, job2;
		cv::Mat frame;

		clock_t start = clock();

		cap.read(frame);
		if (frame.empty())
		{
			stop = true;
			break;
		}

		{
			std::unique_lock<std::mutex> l1(lock1);
			cv1.wait(l1, [&]() { return jobs1.size()<limit; });

			job1.input_image = frame;
			job1.output_image.reset(new std::promise<cv::Mat>());
			jobs1.push(job1);
		}

		{
			std::unique_lock<std::mutex> l2(lock2);
			cv1.wait(l2, [&]() { return  jobs2.size() < limit; });

			job2.input_image = frame;
			job2.output_image.reset(new std::promise<cv::Mat>());
			jobs2.push(job2);
		}

		cv::Mat result1 = job1.output_image->get_future().get();
		cv::Mat result2 = job2.output_image->get_future().get();

		clock_t end = clock();
		std::cout <<"capture: "<< end - start << "ms" << std::endl;

		cv::imshow("result1", result1);
		cv::imshow("result2", result2);
		cv::waitKey(1);
	}
}

void infer1(cv::dnn::Net net)
{ 
	while (true)
	{
		if (stop)
			break; //不加程序无法退出

		if (!jobs1.empty())
		{
			clock_t start = clock();

			std::lock_guard<std::mutex> l1(lock1);
			auto job = jobs1.front();
			jobs1.pop();
			cv1.notify_all();

			cv::Mat input_image = job.input_image, blob, output_image;
			pre_process(input_image, blob);

			std::vector<cv::Mat> network_outputs;
			process(blob, net, network_outputs);

			post_process(input_image, output_image, network_outputs);

			job.output_image->set_value(output_image);

			clock_t end = clock();
			std::cout << "infer1: " << end - start << "ms" << std::endl;
		}
		std::this_thread::yield(); //不加程序无法退出
	}
}

void infer2(cv::dnn::Net net)
{
	while (true)
	{
		if (stop)
			break; //不加程序无法退出

		if (!jobs2.empty())
		{
			clock_t start = clock();

			std::lock_guard<std::mutex> l2(lock2);
			auto job = jobs2.front();
			jobs2.pop();
			cv2.notify_all();

			cv::Mat input_image = job.input_image, blob, output_image;
			pre_process(input_image, blob);

			std::vector<cv::Mat> network_outputs;
			process(blob, net, network_outputs);

			post_process(input_image, output_image, network_outputs);

			job.output_image->set_value(output_image);

			clock_t end = clock();
			std::cout << "infer2: " << end - start << "ms" << std::endl;
		}
		std::this_thread::yield(); //不加程序无法退出
	}
}


int main(int argc, char* argv[])
{
	cv::VideoCapture cap("test.mp4");

	cv::dnn::Net net1 = cv::dnn::readNet("yolov5n.onnx");
	cv::dnn::Net net2 = cv::dnn::readNet("yolov5s.onnx");

	std::thread t0(capture, cap);
	std::thread t1(infer1, net1);
	std::thread t2(infer2, net2);

	t0.join();
	t1.join();
	t2.join();

	return 0;
}

多个视频不同模型同时推理:

#include <iostream>
#include <string>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <windows.h>

#include "yolov5.h"


bool stop = false;

void print_time(std::string video)
{
	auto now = std::chrono::system_clock::now();
	uint64_t dis_millseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count()
		- std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count() * 1000;
	time_t tt = std::chrono::system_clock::to_time_t(now);
	auto time_tm = localtime(&tt);
	char time[100] = { 0 };
	sprintf(time, "%d-%02d-%02d %02d:%02d:%02d %03d", time_tm->tm_year + 1900,
		time_tm->tm_mon + 1, time_tm->tm_mday, time_tm->tm_hour,
		time_tm->tm_min, time_tm->tm_sec, (int)dis_millseconds);
	std::cout << "infer " << video << " 当前时间为:" << time << std::endl;
}

void capture(std::string video, cv::dnn::Net net)
{
	cv::VideoCapture cap(video);
	while (cv::waitKey(1) < 0)
	{
		cv::Mat frame;
		cap.read(frame);

		if (frame.empty())
		{
			stop = true;
			break;
		}

		cv::Mat input_image = frame, blob, output_image;
		pre_process(input_image, blob);

		std::vector<cv::Mat> network_outputs;
		process(blob, net, network_outputs);

		post_process(input_image, output_image, network_outputs);

		print_time(video);
		cv::imshow(video, output_image);
	}
}


int main(int argc, char* argv[])
{
	std::string video1("test1.mp4");
	std::string video2("test2.mp4");

	cv::dnn::Net net1 = cv::dnn::readNet("yolov5n.onnx");
	cv::dnn::Net net2 = cv::dnn::readNet("yolov5s.onnx");

	std::thread t1(capture, video1, net1);
	std::thread t2(capture, video2, net2);

	t1.join();
	t2.join();

	return 0;
}

推理效果如下:
在这里插入图片描述

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

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

相关文章

Unity游戏源码分享-儿童益智数学大脑训练游戏

Unity游戏源码分享-儿童益智数学大脑训练游戏 5秒内选择答案 项目下载地址&#xff1a;https://download.csdn.net/download/Highning0007/88198773

数据结构:双向链表的实现(C实现)

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》 文章目录 前言 一、实现思路1.节点的结构(ListNode)2.新节点的创建(BuyListNode)3.头结点的创建(ListCreate)4.双向链表的销毁(ListDestroy)5.双向链表的打印(ListPrint)6.双向链表的尾插(ListPu…

Vue3项目中使用原生input实现excel导入导出功能

重写input样式 首先我们先来重写input的原生样式&#xff0c;毕竟实在不好看。这里的思路很简单input外面套一层div然后让input撑满盒子然后给input隐藏了就行 <div class"bg-[#f8f8f8] w-[430px] h-[220px] rounded-md cursor-pointer relative outline-0">…

Git详解及使用

Git简介 Git 是一种分布式版本控制系统&#xff0c;它可以不受网络连接的限制&#xff0c;加上其它众多优点&#xff0c;目前已经成为程序开发人员做项目版本管理时的首选&#xff0c;非开发人员也可以用 Git 来做自己的文档版本管理工具。 大概是大二的时候开始接触和使用Gi…

计算机组成原理-笔记-第四章

目录 第四章——指令系统 1、指令格式 &#xff08;1&#xff09;指令&#xff08;机器指令-二进制数&#xff09; &#xff08;2&#xff09;指令分类&#xff08;按照 地址码的数量 分类&#xff09; &#xff08;3&#xff09;指令分类&#xff08;按照长度分类&#x…

Dubbo启动错误

加完Nacos配置后报错 信息: [DUBBO] The registry[<dubbo:registry address"nacos://localhost:8848" protocol"nacos" port"8848" />] will be used as the config center, dubbo version: 2.7.8, current host: 192.168.0.103 八月 09…

【香瓜说职场】如何高效地提问(2018.05.06)

一、什么是低效地提问&#xff1f; 香瓜先举3个非常非常常见的低效提问实例&#xff1a; 1、“为什么我的XXX不成功&#xff1f;” 这个问题就像“为什么我会摔倒”&#xff0c;可能原因有“腿残疾”、“路上有坑”、“眼神不好”等无数种原因……“不摔倒”的我是回答不了的、…

怎么学习JavaScript相关技术? - 易智编译EaseEditing

学习JavaScript相关技术需要一步步地积累知识和实践经验。以下是一些建议的学习步骤和资源&#xff1a; 基础知识&#xff1a; 开始学习JavaScript之前&#xff0c;了解基本的编程概念和术语。你可以通过在线课程、教材或教学视频来学习编程的基础知识。 学习基本语法&#x…

设计模式行为型——模板模式

目录 模板模式的定义 模板模式的实现 模板模式角色 模板模式类图 模板模式举例 模板模式代码实现 模板模式的特点 优点 缺点 使用场景 注意事项 实际应用 模板模式的定义 模板模式&#xff08;Template Pattern&#xff09;属于行为型设计模式&#xff0c;又叫模版…

OpenLayers入门,OpenLayers实现地图原地旋转动画效果

专栏目录: OpenLayers入门教程汇总目录 前言 OpenLayers实现地图原地旋转动画效果,顾名思义,就是站在原地转一圈。 二、依赖和使用 "ol": "^6.15.1"使用npm安装依赖npm install ol@6.15.1使用Yarn安装依赖yarn add olvue中如何使用: vue项目使用…

opencv基础 42- Scharr算子-cv2.Scharr()(边缘检测基础)

Scharr算子是用于计算图像梯度的一种常用算子&#xff0c;特别是在边缘检测任务中。它是Sobel算子的改进版本&#xff0c;旨在提供更加准确和敏感的边缘检测。 在离散的空间上&#xff0c;有很多方法可以用来计算近似导数&#xff0c;在使用 33 的 Sobel 算子时&#xff0c;可能…

Android进阶之SeekBar动态显示进度

SeekBar 在开发中并不陌生,默认的SeekBar是不显示进度的,当然用吐司或者文案在旁边实时显示也是可以的,那能不能移动的时候才显示&#xff0c;默认不显示呢,当然网上花哨的三方工具类太多了&#xff0c;但是我只是单纯的想在SeekBar的基础上去添加一个可以跟随移动显示的气泡而…

循环结构的学习

循环结构 文章目录 为什么要学习循环while循环dowhile循环偶数之和断点调试购物结算循环的选择类名和全类名摄氏华氏对照表for循环for执行次序五门功课成绩for的特殊写法break和continue录入客户信息_continue使代码优雅小数的比较不能用或! 为什么要学习循环 在编写代码时&a…

mysql死锁分析show engine innodb status

文章目录 1、show engine innodb status命令2、前置知识3、无符号数、有符号数4、innodb关于有符号数的规定5、为什么会有这个规定 1、show engine innodb status命令 最近在使用mysql的show engine innodb status命令分析死锁&#xff0c;发现了一个有意思的点。就是红框里圈…

【小梦C嘎嘎——启航篇】类和对象(中篇)

【小梦C嘎嘎——启航篇】类和对象&#xff08;中篇&#xff09;&#x1f60e; 前言&#x1f64c;类的6个默认成员函数构造函数析构函数拷贝构造函数拷贝构造函数的特性有哪些&#xff1f;既然编译器可以自动生成一个拷贝构造函数&#xff0c;为什么我们还要自己设计实现呢&…

python数据分析 期末测验,python数据分析基础题库

大家好&#xff0c;小编为大家解答python数据分析选择题题目的问题。很多人还不知道python数据分析题目和答案&#xff0c;现在让我们一起来看看吧&#xff01; 自测试卷 5 一、选择题 1 &#xff0e;下面关于 RFM 模型说法正确的是&#xff08; &#xff09;。 A &#xff0e;…

微信小程序(由浅到深)

文章目录 一. 项目基本配置1. 项目组成2. 常见的配置文件解析3. app.json全局的五大配置4.单个页面中的page配置5. App函数6.tabBar配置 二. 基本语法&#xff0c;事件&#xff0c;单位1. 语法2. 事件3. 单位 三. 数据响应式修改四 . 内置组件1. button2. image3. input4. 组件…

钓鱼攻击:相似域名识别及如何有效预防攻击

网络犯罪分子很乐意劫持目标公司或其供应商或业务合作伙伴的官方域名&#xff0c;但在攻击的早期阶段&#xff0c;他们通常没有这种选择。相反&#xff0c;在有针对性的攻击之前&#xff0c;他们会注册一个与受害组织的域名相似的域名 - 他们希望您不会发现其中的差异。此类技术…

opencv基础51-图像轮廓04-轮廓多种图形拟合讲解及示例

轮廓拟合&#xff08;Contour Fitting&#xff09;是指在图像处理中&#xff0c;通过一些数学模型或曲线拟合方法来近似描述图像中的轮廓。图像中的轮廓是指物体的边界&#xff0c;轮廓拟合可以用于提取、描述和分析物体的形状。 常用的轮廓拟合方法包括多项式拟合、贝塞尔曲线…

【刷题笔记8.9】LeetCode题目:两数相加

LeetCode题目2&#xff1a;两数相加 题目及描述 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设…