栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

树莓派搭建http服务器实现网页监控摄像头

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

树莓派搭建http服务器实现网页监控摄像头

目录

前言

一、HTTP协议

1.客户端请求

 2.服务器响应

3.常见响应代号和对应描述

二、服务器搭建

1.思路

2.获取请求头部

 3.解析头部获取URL和请求

4.服务器响应 

 5.http.h

6.tcp.cpp 和tcp.h 

 三、主程序的实现

 四、本地html文件

总结


前言

在树莓派部署yolov5lite的基础上,搭建http服务器,添加网页监控功能


一、HTTP协议

http(hyper text transfer prtocol)即超文本传输协议,利用该协议能够实现浏览器加载本地文件的功能,本文在http1.0版本的基础上实现功能。

1.客户端请求

客户端请求的格式一般由:请求行、请求头、空行和请求数据组成,具体格式如下:

请求行:        请求方法|空格|URL|空格|协议版本|回车符|换行符|

请求头:        字段名|:|值|回车符|换行符|

                       ........

                       字段名|:|值|回车符|换行符|

空行:            回车符|换行符|

请求数据:     xxxxxxxx

 2.服务器响应

服务器响应的格式一般由:状态行、消息头、空行和响应正文组成,具体格式如下:

状态行:        协议版本|空格|响应代号|空格|代号描述|回车符|换行符|

消息头:        字段名|:|值|回车符|换行符|

                       ........

                       字段名|:|值|回车符|换行符|

空行:            回车符|换行符|

响应正文:     xxxxxxxx

3.常见响应代号和对应描述
响应代号代号描述
服务器上存在请求内容并可以响应给客户端200OK
客户端请求异常,方法有问题501METHOD NOT IMPLEMENTED
请求内容不存在404NOT FOUND
客户端发送的请求格式有问题400BAD REQUEST

二、服务器搭建

1.思路

http协议工作模式为客户端发送请求,服务器响应请求,客户端为浏览器,因此只需要解析客户端发来的请求并响应即可,由于yolov5在c++环境下部署,因此服务器采用c/c++语言。

2.获取请求头部

简历socket连接,read客户端发来的消息并解析

int HTTP::getline(int socketid, string &buff)
{
	while(true)//遇到换行说明一行结束
	{
		char ch = '';
		int len = read(socketid, &ch, 1);

		if(len == 1)//正常读取
		{
			if(ch == 'r') 			continue;
			else if (ch == 'n') 	break;

			buff += ch;//string添加
		}
		else if (len == -1)//读取出错
		{
			perror("read error");
			return -1;
		}
		else if (len == 0)//无数据
		{
			std::cerr<<"关闭客户端"< 0);
	cout< 

 3.解析头部获取URL和请求

根据请求头格式,先读取一行请求行,利用空格划分字符串并获取URL和请求,如果URL格式不正确,比如url=index.htm?xxxxxxxxx,只取?前的内容

*
作用:解析请求行内容
形参:请求行内容buff,请求方法缓存request,url路径缓存
返回值:解析成功返回true,否则false
*/
bool HTTP::get_request_url(string buff, string &request, string &url)
{
	if(buff == "")
		return false;

	std::vector strlist;
	strlist.clear();
	buff += " ";
	size_t pos = buff.find(' ');

	while(pos != buff.npos)
	{
		strlist.push_back(buff.substr(0, pos));
		buff = buff.substr(pos+1, buff.size());
		pos = buff.find(' ');
	}

	request = strlist[0];
	url = strlist[1];
	cout<<"request: "< 

4.服务器响应 

根据请求方法和URL进行响应,由于本文只用到了GET方法,故只需要解决GET和非GET

void HTTP::do_response(int socketid, std::string url, int status)
{
	cout<<"==============================do_response========================"< 0)//读取正常
	{
		
		if(!get_request_url(buff, request, url))//解析错误
			return;
		if(request == "GET")
		{
			int status = 0;
			do_response(socketid, url, status);
		}
		else//非GET请求,读取http头部,并响应客户端501 Mehtod Not Implemented
		{

			do_response(socketid, 501);
		}
	}
	else//读取异常
	{
		do_response(socketid, 400);
	}
}

 5.http.h
#ifndef __HTTP_H
#define __HTTP_H

#include 
#include "tcp.h"
#include "http.h"
#include "sys/stat.h"
#include 
#include 
#include 

class HTTP
{
	private:
		const std::string status_200 = "HTTP/1.0 200 OKrn";
		const std::string status_501 = "HTTP/1.0 501 METHOD NOT IMPLEMENTEDrn";
		const std::string status_404 = "HTTP/1.0 404 NOT FOUNDrn";
		const std::string status_400 = "HTTP/1.0 400 BAD REQUESTrn";
		const std::string text_header = "Server: Run ServerrnContent-Type: text/htmlrnConnection: closernContent-Length: ";
		const std::string img_header = "Server: Run ServerrnContent-Type: image/jpegrnConnection: closernContent-Length: ";
		const std::string work_dir = "/home/sy/HTTPCPP/html_docs";
    public:
		int getline(int socketid, std::string &buff);
		int gethead(int socketid, std::string &header);
		bool get_request_url(std::string buff, std::string &request,std::string &url);
		long int get_realurl(std::string &url, int &status);
		void do_request(int socketid);
		void do_response(int socketid, std::string url, int status);
		void do_response(int socketid, int status);
};

#endif

6.tcp.cpp 和tcp.h 
#include "tcp.h"

//domain 指定使用何种的地址类型
//PF_INET/AF_INET  Ipv4 网络协议
//PF_INET6/AF_INET6  Ipv6 网络协议
int TCP::Socket(int domain,int type,int protocol)
{
	int socketid = socket(domain,type,protocol);
	if(socketid == -1)
	{
		perror("socket");
		exit(1);
	}
	return socketid;
}

int TCP::Bind(int sockfd,struct sockaddr * my_addr,int addrlen)
{
	int val = bind(sockfd,my_addr,addrlen);
	if(val == -1)
	{
		perror("bind");
		exit(0);
	}
	return val;
}

int TCP::Listen(int s,int backlog)
{
	int val = listen(s,backlog);
	if(val == -1)
	{
		perror("listen");
		exit(0);
	}
	return val;
}

int TCP::Accept(int s,struct sockaddr * addr,socklen_t * addrlen)
{
	int newfd = accept(s,addr,addrlen);
	if(newfd < 0)
	{
		perror("accept");
		exit(0);
	}
	return newfd;
}

int TCP::Connect(int s,struct sockaddr * addr,socklen_t addrlen)
{
	int newfd = connect(s, addr, addrlen);
	if(newfd < 0)
	{
		perror("connet");
		exit(0);
	}
	return newfd;
}

#ifndef TCP_H
#define TCP_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


class TCP{
public:
		int Socket(int domain,int type,int protocol);
		int Bind(int sockfd,struct sockaddr * my_addr,int addrlen);
		int Listen(int s,int backlog);
		int Accept(int s,struct sockaddr * addr,socklen_t * addrlen);
		int Connect(int s,struct sockaddr * addr,socklen_t addrlen);
};

#endif

 三、主程序的实现

树莓派搭建服务器采用多线程的方式,主程序负责人脸识别并保存jpg格式图片,创建线程用来实现图片和html文件传输,设置全局变量标志位,当图像文件写入完成后,线程才能发送图片,而发送图片时不能写入。

#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include "Yolo.h"
#include "base64.h"
#include "tcp.h"
#include "http.h"
#include 

bool sendok = false;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void show_shape(std::vector shape)
{
    std::cout< &boxes, int w_from, int h_from, int w_to, int h_to)
{
    float w_ratio = float(w_to)/float(w_from);
    float h_ratio = float(h_to)/float(h_from);
    
    
    for(auto &box: boxes)
    {
        box.x1 *= w_ratio;
        box.x2 *= w_ratio;
        box.y1 *= h_ratio;
        box.y2 *= h_ratio;
    }
    return ;
}

cv::Mat draw_box(cv::Mat & cv_mat, std::vector &boxes)
{
    int CNUM = 80;
    static const char* class_names[] = {
        "face", "face_mask"
    };
    cv::RNG rng(0xFFFFFFFF);
	cv::Scalar_ randColor[CNUM];
	for (int i = 0; i < CNUM; i++)
		rng.fill(randColor[i], cv::RNG::UNIFORM, 0, 256);
    
    for(auto box : boxes)
    {
        int width = box.x2-box.x1;
        int height = box.y2-box.y1;
        char text[256];
        cv::Point p = cv::Point(box.x1, box.y1-5);
        cv::Rect rect = cv::Rect(box.x1, box.y1, width, height);
        cv::rectangle(cv_mat, rect, cv::Scalar(0, 0, 255), 2);
        sprintf(text, "%s %.1f%%", class_names[box.label], box.score * 100);
        cv::putText(cv_mat, text, p, cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255), 2);
    }
    return cv_mat;
}

void* pthread_listen(void* arg)
{
	TCP tcp;
	HTTP http;

	int socketid = tcp.Socket(AF_INET, SOCK_STREAM, 0);

	struct sockaddr_in httpserver;
	httpserver.sin_family = AF_INET;
	httpserver.sin_port = htons(80);
	httpserver.sin_addr.s_addr = htonl(INADDR_ANY);
	tcp.Bind(socketid, (struct sockaddr *)&httpserver, sizeof(struct sockaddr));
	tcp.Listen(socketid, 100);

	while (true)
	{
		struct sockaddr_in client;
		socklen_t clientlen = sizeof(client);
		char client_ip[64] = {0};

		
		int client_sockfd = tcp.Accept(socketid, (struct sockaddr *)&client, &clientlen);
		cout << "========================================================" << endl;
		cout << "client ip: " << inet_ntop(AF_INET, &client.sin_addr.s_addr, client_ip, sizeof(client_ip)) << "tport : " << ntohs(client.sin_port) << endl;
		while(!sendok);
		http.do_request(client_sockfd);
		sendok = false;
		close(client_sockfd);
	}
	close(socketid);
	pthread_exit(NULL);
}

int main(int argc, char* argv[])
{
	Base64 base64;
    std::string model_name = "weights/yolov5s.mnn";

    int num_classes=2;
    std::vector yolov5s_layers{
			{"461",    32, {{116, 90}, {156, 198}, {373, 326}}},	
            {"403",    16, {{30,  61}, {62,  45},  {59,  119}}},	
            {"345", 	8,  {{10,  13}, {16,  30},  {33,  23}}},	
			
    };
    std::vector & layers = yolov5s_layers;

    int net_size =640;
    // get output data
    std::string output_tensor_name0 = layers[2].name ;
    std::string output_tensor_name1 = layers[1].name ;
    std::string output_tensor_name2 = layers[0].name ;
    
    std::shared_ptr net = std::shared_ptr(MNN::Interpreter::createFromFile(model_name.c_str()));
    if (nullptr == net) {
        return 0;
    }

    MNN::ScheduleConfig config;
    config.numThread = 4;
    config.type      = static_cast(MNN_FORWARD_CPU);
    MNN::BackendConfig backendConfig;
    backendConfig.precision = (MNN::BackendConfig::PrecisionMode)2;
    // backendConfig.precision =  MNN::PrecisionMode Precision_Normal; // static_cast(Precision_Normal);
    config.backendConfig = &backendConfig;
    
    std::vector saveNamesVector;
    saveNamesVector.push_back(output_tensor_name0);
    saveNamesVector.push_back(output_tensor_name1);
    saveNamesVector.push_back(output_tensor_name2);
    
    config.saveTensors = saveNamesVector;
    
    MNN::Session *session = net->createSession(config);
    
	cv::VideoCapture capture;
    capture.open(0);  //修改这个参数可以选择打开想要用的摄像头

    cv::Mat frame;
	int INPUT_SIZE = 320;
	std::string img;
	pthread_mutex_init(&lock, NULL);
	pthread_t id;
	pthread_create(&id, NULL, pthread_listen, NULL);
	while(1)
	{ 
		capture >> frame;
		cv::Mat image;
		cv::flip(frame, image, 1);
		cv::Mat raw_image = image;

		cv::resize(raw_image, image, cv::Size(INPUT_SIZE, INPUT_SIZE));
		// preprocessing
		image.convertTo(image, CV_32FC3);
		// image = (image * 2 / 255.0f) - 1;
		image = image /255.0f;

		// wrapping input tensor, convert nhwc to nchw    
		std::vector dims{1, INPUT_SIZE, INPUT_SIZE, 3};
		auto nhwc_Tensor = MNN::Tensor::create(dims, NULL, MNN::Tensor::TENSORFLOW);
		auto nhwc_data   = nhwc_Tensor->host();
		auto nhwc_size   = nhwc_Tensor->size();
		std::memcpy(nhwc_data, image.data, nhwc_size);

		auto inputTensor = net->getSessionInput(session, nullptr);
		inputTensor->copyFromHostTensor(nhwc_Tensor);

		// run network
		clock_t startTime,endTime;
		startTime = clock();//计时开始
		net->runSession(session);
		
		
		MNN::Tensor *tensor_scores  = net->getSessionOutput(session, output_tensor_name0.c_str());
		MNN::Tensor *tensor_boxes   = net->getSessionOutput(session, output_tensor_name1.c_str());
		MNN::Tensor *tensor_anchors = net->getSessionOutput(session, output_tensor_name2.c_str());

		MNN::Tensor tensor_scores_host(tensor_scores, tensor_scores->getDimensionType());
		MNN::Tensor tensor_boxes_host(tensor_boxes, tensor_boxes->getDimensionType());
		MNN::Tensor tensor_anchors_host(tensor_anchors, tensor_anchors->getDimensionType());

		tensor_scores->copyToHostTensor(&tensor_scores_host);
		tensor_boxes->copyToHostTensor(&tensor_boxes_host);
		tensor_anchors->copyToHostTensor(&tensor_anchors_host);

		std::vector result;
		std::vector boxes;
		
		yolocv::YoloSize yolosize = yolocv::YoloSize{INPUT_SIZE,INPUT_SIZE};
		
		float threshold = 0.45;
		float nms_threshold = 0.5;

		// show_shape(tensor_scores_host.shape());
		// show_shape(tensor_boxes_host.shape());
		// show_shape(tensor_anchors_host.shape());

		
		boxes = decode_infer(tensor_scores_host, layers[2].stride,  yolosize, net_size, num_classes, layers[2].anchors, threshold);
		result.insert(result.begin(), boxes.begin(), boxes.end());

		boxes = decode_infer(tensor_boxes_host, layers[1].stride,  yolosize, net_size, num_classes, layers[1].anchors, threshold);
		result.insert(result.begin(), boxes.begin(), boxes.end());

		boxes = decode_infer(tensor_anchors_host, layers[0].stride,  yolosize, net_size, num_classes, layers[0].anchors, threshold);
		result.insert(result.begin(), boxes.begin(), boxes.end());

		nms(result, nms_threshold);

		std::cout< vecImg;
		std::vector quality;
		quality.push_back(CV_IMWRITE_JPEG_QUALITY);
		quality.push_back(30);
		cv::imencode(".jpg",frame_show, vecImg, quality);
			
		if(!sendok)
			cv::imwrite("html_docs/output.jpg", frame_show);
		sendok = true;

		//cv::imshow("outpt", frame_show);
		//if (cv::waitKey(30) >= 0)
		//	break;
    }
    return 0;
}

 四、本地html文件

接收到客户端请求后,会将本地的html文件进行传输,从而实现网页,利用刷新机制,设置图片为200ms刷新一次,从而实现视频效果。

  • 响应代码为200的html



Raspi




Raspi Camera




  • 响应代码为400的html



400 BAD REQUEST



400 BAD REQUEST
请求格式有问题!



  • 响应代码为404的html



404 NOT FOUND



404 NOT FOUND
File is not existed!



  •  响应代码为501的html



501 METHOD NOT IMPLEMENTED



501 METHOD NOT IMPLEMENTED
请求方法异常!




总结

最终实现效果如图

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1002966.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号