利用gRPC C++传输opencv的mat类型图片-第二种高效率方法---我是搬砖大学生

前言

上次在另外一编文章写了关于利用gRPC c++传输图片的方法。详情请看:

https://blog.csdn.net/liyangbinbin/article/details/100538412

但是效率不高,因为无论服务器还是客户端都要进行M*N(图像大小是MxN)次的循环,对于小图像来说还好,但是对于大图像来讲,效率不怎么好,后来看到了以复制内存块的方法,就是每次复制一行,这样就能减少循环次数了,BUT。。。靓仔都会懂的,指针这玩意动不动就是非法访问,指向出错,这次行了,下次又不知道什么鬼不行了,就算是行了,再反解回mat图片时图片都变形了,搞得我是十分没有脾气,大喊扑街了,后来看到了capcop博主的一篇文章,详情:

https://blog.csdn.net/tt_ren/article/details/53227900

原来opencv里面还有个解码编码的函数imdecode/imencode,实在是太好用了,根本不用去这么复杂的操作内存,去管理指针了。直接上代码

客户端代码

#pragma comment(lib,"ws2_32.lib")
#include <iostream>
#include <fstream>
#include <string>
#include <grpc/grpc.h>
#include <grpc++/server.h>
#include <grpc++/server_builder.h>
#include <grpc++/server_context.h>
#include <grpc++/security/server_credentials.h>
#include "uppic.grpc.pb.h"
#include <grpc++/channel.h>
#include <grpc++/client_context.h>
#include <grpc++/create_channel.h>
#include "opencv.hpp"
#include <memory.h>
#include <conio.h>
#include <stdio.h>
using grpc::Status;
using grpc::Channel;
using grpc::ClientContext;
using grpc::ClientWriter;
using namespace namespace_uploadpic;
using grpc::ClientContext;
#define GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH(INT_MAX)
class uppicIml
{
public:
	//构造函数,创建一个频道,用于指向服务器
	uppicIml(std::shared_ptr<Channel>channl) :stu_(upload_pic_servicer::NewStub(channl)) {}
	void uppp()
	{
		//创建一个ChunkOneLine*的vector,用于存储图像的数据
		std::vector<ChunkOneLine*>chunkonelie;
		//读入一个图片
		cv::Mat img = cv::imread("C:/Users/Administrator/Desktop/888.bmp");
		 ChunkOneLine *sedchunk = new ChunkOneLine();
		 Chunk *chunk=new Chunk();
		 //记录当前时间
		 std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now();
		 //编码的前必须的data格式,用一个uchar类型的vector
		 std::vector<uchar> data_encode;
		 //直接编码
		 cv::imencode(".jpg", img, data_encode);
		 std::cout << "size: " << sizeof(data_encode) << std::endl;
		 //放到string里面
		 std::string str_encode(data_encode.begin(), data_encode.end());
		 //把得到的string放到buff里面,
		 chunk->set_allocated_buff(&str_encode);
		 sedchunk->set_allocated_databuf(chunk);
		ClientContext context;
		//定义一个用来存储返回信息的变量
		Reply reply;
		//获得远程API(俗称远程方法)的指针
		std::unique_ptr<ClientWriter<::namespace_uploadpic::ChunkOneLine>> writer=stu_->Upload(&context, &reply);
		//开始写(发送)
		if (!writer->Write(*sedchunk))
		{
			//break;
			std::cout << "error!\n";
		}
		
		//写完了
		writer->WritesDone();
		//等待服务器回复
		grpc::Status status = writer->Finish();

		//再次记录当前时间
		std::chrono::system_clock::time_point end_time = std::chrono::system_clock::now();
		//duration_cast是个模板类,可以自定义转换类型,milliseconds就是要转换的单位,
		//(end_time - start_time)把它转换成milliseconds,也就是毫秒
		auto sec = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
		if (status.ok())
		{
			std::cout << "数据传输完成\n";
			std::cout << "传输时间为:" <<sec.count();
		}
		else
		{
			std::cout << "数据传输失败\n";
		}
	
	}
	
private:
	//这个是远程方法(API)的一个指针
	std::unique_ptr<upload_pic_servicer::Stub>stu_;
};
int main()
{
	//定义一类并初始化
	//CreateChannel是创建一个频道,里面包括远程主机的地址和商品,第二个表示不加密
	uppicIml upppp(grpc::CreateChannel("127.0.0.1:50051", grpc::InsecureChannelCredentials()));
	//我们的方法写
	upppp.uppp();
	system("pause");
	return 0;
}

服务端代码

#include<string>
#include <iostream>
#include <grpc/grpc.h>
#include <grpc++/server.h>
#include <grpc++/server_builder.h>
#include <grpc++/server_context.h>
#include <grpc++/security/server_credentials.h>
#include "uppic.grpc.pb.h"
#include <time.h>
#include <chrono>
#include "opencv.hpp"
#include <thread>
#include <chrono>
//命名空间
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::ServerReader;
using grpc::Status;
using grpc::Channel;
using namespace namespace_uploadpic;
using grpc::ClientContext;
#define GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH(INT_MAX)
void disyly(cv::Mat mat)
{
	cv::imshow("aa", mat);
	cv::waitKey(1);
}
class upPicserver final :public namespace_uploadpic::upload_pic_servicer::Service
{
public:
	//这个Upload是重写了rpc里面的方法
	Status Upload(ServerContext *context, ServerReader<ChunkOneLine> *reader, Reply *reply);
};
Status upPicserver::Upload(ServerContext *context, ServerReader<ChunkOneLine> *reader, Reply *reply)
{
	//记录当前时间
	std::chrono::system_clock::time_point start_time =std::chrono::system_clock::now();
	//定义接收的对象
	ChunkOneLine oneLie;
	
	//读
	if (!reader->Read(&oneLie))
	{
		std::cout << "接收失败\n";
	}
	cv::Mat mat;
	std::string str_decon;
	//把接收到的buff用str_decon存储,其实在protobuf这个IDL语言里,bytes对应的就是string
	str_decon = oneLie.databuf().buff();
	//解码的前必须的data格式,用一个uchar类型的vector
	std::vector<uchar>data(str_decon.begin(),str_decon.end());
	//直接解码
	mat = cv::imdecode(data, 1);
	//enum {
	//	CV_LOAD_IMAGE_UNCHANGED = -1, /* 8 bit, color or not */
	//	CV_LOAD_IMAGE_GRAYSCALE = 0,  /* 8 bit, gray */
	//	CV_LOAD_IMAGE_COLOR = 1,      /* ?, color */
	//	CV_LOAD_IMAGE_ANYDEPTH = 2,   /* any depth, ? */
	//	CV_LOAD_IMAGE_ANYCOLOR = 4    /* ?, any color */
	//};
 
	//再次记录当前时间
	std::chrono::system_clock::time_point end_time = std::chrono::system_clock::now();
	//duration_cast是个模板类,可以自定义转换类型,milliseconds就是要转换的单位,
	//(end_time - start_time)把它转换成milliseconds,也就是毫秒
	auto sec = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
	
	
	//把消耗的时间返回给客户端
	reply->set_length(sec.count());
	cv::imshow("bb", mat);
	//cv::moveWindow("bb", 100, 100);
	cv::waitKey(1);
	return grpc::Status::OK;
}
int main()
{
	//创建一个用于响应的类
	upPicserver service;
	//监听的端口,前面的IP地址,似乎只有0,0,0,0和127.0.0.1可用
	//应该是代表本地的IP吧
	std::string add_ip("0.0.0.0:50051");
	//创建一个服务类
	ServerBuilder builder;
	//监听,后面那个参数代表不使用ssl加密
	builder.SetMaxReceiveMessageSize(INT_MAX);
	//builder.SetMaxSendMessageSize(INT_MAX);
	builder.AddListeningPort(add_ip, grpc::InsecureServerCredentials());
	//把我们自己写的响应的类挂上去
	builder.RegisterService(&service);
 
	//开始
	std::unique_ptr<Server>server(builder.BuildAndStart());
	
	std::cout << "Server listening on " << add_ip << std::endl;
	server->Wait();
 
 
	return 0;
}

结果

亲测使用笔记本的摄像头作为图像输入,速度可达40-60毫秒之内,基本上可以当网络摄像头了,效果还是不错的

  • 1
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
grpc-server-spring-boot-starter是一个基于Spring Boot框架的gRPC服务器的启动器。gRPC(Google Remote Procedure Call)是一种高性能的远程过程调用框架,它使用Protocol Buffers作为接口定义语言,并支持多种编程语言。 grpc-server-spring-boot-starter提供了一系列简化配置和集成的功能,使得在Spring Boot应用中启动和配置gRPC服务器变得更加容易。它提供了自动装配的功能,可以根据应用的配置自动创建和启动gRPC服务器。用户只需要在配置文件中设置相应的参数,如服务器的端口号、TLS证书等,即可完成服务器的启动配置。 在使用grpc-server-spring-boot-starter时,用户可以方便地定义服务接口和实现类。通过使用gRPC的接口定义语言(protobuf)定义接口,并生成对应的Java代码。然后,用户只需要在实现类中实现相应的接口方法即可。 在服务器启动后,grpc-server-spring-boot-starter会根据定义的接口和实现类,自动创建相应的gRPC服务,并将其注册到服务器中。当客户端发起远程调用时,服务器会根据接口定义和方法参数,将请求转发给对应的实现类,并返回执行结果给客户端。 grpc-server-spring-boot-starter还支持对gRPC服务器进行拦截器的配置。拦截器可以在请求和响应的过程中拦截和修改消息,用于实现日志记录、鉴权、性能监控等功能。 总之,grpc-server-spring-boot-starter简化了在Spring Boot应用中使用gRPC的配置和集成过程,使得开发者可以更加便捷地构建和部署gRPC服务器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值