【视频传输】二、Opencv结合socket进行视频传输(TCP协议)

        博文由来笔者突发奇想,做采集4USB摄像头画面小实验时,却遇到了在电脑上最多只能同时开启3个这样头痛的问题(个人分析认为是电脑的问题),故出此下策,在客户端挂1个,服务器挂3个摄像头,利用socket进行视频传输,本篇文章是利用的是TCP协议。笔者拙见,欢迎提出批评。

        说明:在此之前笔者也从未接触过socket编程,也不懂TCP/IPUDP等基本知识。为了避免让读者感到繁琐,我将运用小学及初中学习的总分总、承上启下的写作技巧进行讲解,力图做到详略有致,言简意赅,通俗易懂。如确实还有问题,可以直接回复博客或图文并茂的将问题邮件给笔者(cuit_hjs@163.com),笔者知识有限,但尽量答复。

        实验平台:VS2010 + opencv2.4.10(其他版本搭配也是一样的做法)

        准备工作:

              1一台电脑,能跑vsopencv,用来运行服务器(server)和客户端(client)代码;

              2、一个摄像头(笔记本摄像头、USB摄像头等,网络摄像头我还没接触过,暂不考虑)。

        在同一台电脑上实验,即运行服务器程序,又跑客户端程序,也就是说通过socket编程来实现数据的自发自收,这一步通过了接下来跑服务器和客户端分开的实验就简单了。两台PC之间的视频传输见下一节:【二】Opencv结合socket进行视频传输(TCP协议)

        那我们先在就开始吧,不用等下一曲了。

        实验1简单的数据传输,分三步。

              建立一个名为servervs空项目,再新建demo.cpp源文件,将下面的代码拷贝到demo.cpp中,编译好后关闭vs

#include "stdafx.h"
#include <stdio.h>
#include <winsock2.h>

#pragma comment(lib,"ws2_32.lib")

int main(void)
{
	//初始化WSA
	WORD sockVersion = MAKEWORD(2,2);
	WSADATA wsaData;
	if(WSAStartup(sockVersion, &wsaData)!=0)
	{
		return 0;
	}

	//创建套接字
	SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(slisten == INVALID_SOCKET)
	{
		printf("socket error !");
		return 0;
	}

	//绑定IP和端口
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(8888);
	sin.sin_addr.S_un.S_addr = INADDR_ANY; 
	if(bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
	{
		printf("bind error !");
	}

	//开始监听
	if(listen(slisten, 5) == SOCKET_ERROR)
	{
		printf("listen error !");
		return 0;
	}

	//循环接收数据
	SOCKET sClient;
	sockaddr_in remoteAddr;
	int nAddrlen = sizeof(remoteAddr);
	char revData[255]; 
	while (true)
	{
		printf("等待连接...\n");
		sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
		if(sClient == INVALID_SOCKET)
		{
			printf("accept error !");
			continue;
		}
		printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
		
		//接收数据
		int ret = recv(sClient, revData, 255, 0);		
		if(ret > 0)
		{
			revData[ret] = 0x00;
			printf(revData);
		}

		//发送数据
		char * sendData = "你好,TCP客户端!\n";
		send(sClient, sendData, strlen(sendData), 0);
		closesocket(sClient);
	}
	
	closesocket(slisten);
	WSACleanup();
	return 0;
}

               建立一个名为clientvs空项目,再新建demo.cpp源文件,将下面的代码拷贝到demo.cpp中,编译好后关闭vs

#include "stdafx.h"
#include <WINSOCK2.H>
#include <STDIO.H>

#pragma  comment(lib,"ws2_32.lib")

int main(int argc, char* argv[])
{
	WORD sockVersion = MAKEWORD(2,2);
	WSADATA data; 
	if(WSAStartup(sockVersion, &data) != 0)
	{
		return 0;
	}

	SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(sclient == INVALID_SOCKET)
	{
		printf("invalid socket !");
		return 0;
	}

	sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(8888);
	serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); 
	if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
	{
		printf("connect error !");
		closesocket(sclient);
		return 0;
	}
	char * sendData = "你好,TCP服务端,我是客户端!\n";
	send(sclient, sendData, strlen(sendData), 0);

	char recData[255];
	int ret = recv(sclient, recData, 255, 0);
	if(ret > 0)
	{
		recData[ret] = 0x00;
		printf(recData);
	}
	closesocket(sclient);
	WSACleanup();
	return 0;
}

               同时打开serverclient这两个解决方案。先运行server;再运行client,你会发现二者建立起了通信,并相互之间成功将数据进行了传输。

        到此我们已经学会了怎么使用socket进行发送接收数据了,这里有两个函数send()recv()。注意:127.0.0.1这个IP地址是回送地址(自发自收),每台电脑的回环地址都是这个IP,是固定的。很多时候我们组装电脑时给电脑插网卡,就可以在cmd窗口里面pingIP,若不通则说明网卡没插好。

        实验2简单的图片传输,分三步。

                   接下来我们就在实验1的基础上添加入opencv代码,读取图片,为尽量简单,我们将图像灰度化。设置好opencv的include和lib路径及依赖项后,就可以开始动手敲代码了。

              先看client端,思路是将读出的图像数据放到一个数组里面,再用send()发送出去,代码如下:

#include "stdafx.h"
#include <WINSOCK2.H>
#include <iostream>
#include <stdio.h>
#include <cv.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#pragma  comment(lib,"ws2_32.lib")

int main(int argc, char* argv[])
{
	WORD sockVersion = MAKEWORD(2,2);
	WSADATA data; 
	if(WSAStartup(sockVersion, &data) != 0)
	{
		return 0;
	}

	SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(sclient == INVALID_SOCKET)
	{
		printf("invalid socket !\n");
		return 0;
	}

	sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(8888);
	serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); 
	if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
	{
		printf("connect error !\n");
		closesocket(sclient);
		return 0;
	}
	//读取图像并发送
	IplImage *image_src = cvLoadImage("wall.jpg");//载入图片
	IplImage *image_dst = cvCreateImage(cvSize(640, 480), 8, 1);//放置灰度图像

	int i, j;
	char sendData[1000000] = "";
	cvNamedWindow("client", 1);
	cvCvtColor(image_src, image_dst, CV_RGB2GRAY);
	for(i = 0; i < image_dst->height; i++)
	{
		for (j = 0; j < image_dst->width; j++)
		{
			sendData[image_dst->width * i + j] = ((char *)(image_dst->imageData + i * image_dst->widthStep))[j];
		}
	}
	cvShowImage("client", image_dst);
	cvWaitKey(0);//任意键发送
	send(sclient, sendData, 1000000, 0);//发送
	cvDestroyWindow("client");
	closesocket(sclient);
	WSACleanup();
	return 0;
}

          server端,思路是将recv()接收到的数据放到一个数组里面,再还原为图像数据,代码如下:

#include "stdafx.h"
#include <stdio.h>
#include <winsock2.h>
#include <cv.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#pragma comment(lib,"ws2_32.lib")

int main(int argc, char* argv[])
{
	//初始化WSA
	WORD sockVersion = MAKEWORD(2,2);
	WSADATA wsaData;
	if(WSAStartup(sockVersion, &wsaData)!=0)
	{
		return 0;
	}
	//创建套接字
	SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(slisten == INVALID_SOCKET)
	{
		printf("socket error !");
		return 0;
	}
	//绑定IP和端口
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(8888);//端口8888
	sin.sin_addr.S_un.S_addr = INADDR_ANY; 
	if(bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
	{
		printf("bind error !");
	}

	//开始监听
	if(listen(slisten, 5) == SOCKET_ERROR)
	{
		printf("listen error !");
		return 0;
	}

	//循环接收数据
	SOCKET sClient;
	sockaddr_in remoteAddr;
	int nAddrlen = sizeof(remoteAddr);

	printf("等待连接...\n");
	do
	{
		sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
	}while(sClient == INVALID_SOCKET);
	printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
	
	char revData[1000000] = ""; 
	IplImage *image_src = cvCreateImage(cvSize(640, 480), 8, 1);//接收灰度图像
	int i, j;
	int ret;
	cvNamedWindow("server", 1);
	while(true)
	{
		//接收数据
		ret = recv(sClient, revData, 1000000, 0);		
		if(ret > 0)
		{
			revData[ret] = 0x00;
			for(i = 0; i < image_src->height; i++)
			{
				for (j = 0; j < image_src->width; j++)
				{
					((char *)(image_src->imageData + i * image_src->widthStep))[j] = revData[image_src->width * i + j];
				}
			}
			ret = 0;
		}
		cvShowImage("server", image_src);
		cvWaitKey(1);
	}
	cvDestroyWindow("server");	
	closesocket(slisten);
	WSACleanup();
	return 0;
}

         先运行server,再运行client。在client端,按任意键即可发送数据,而server端马上会收到数据并还原为图像显示出来。

        经过上述的实验及讲解,可以对使用socket的编程有一定的了解。如果能够启发读者想去探索,并开发些好玩的东西出来,那自然是一件幸事,但这里就不深入了。

       本次实验所需要的所有文件都已打包上传,此处下载:http://download.csdn.net/detail/hujingshuang/8400083

       特别注意:

               1、本博客例程仅做学习交流用,切勿用于商业用途。

               2、欢迎交流,转载请注明出处:http://blog.csdn.net/hujingshuang/article/details/43191461

 

 

 

 

 

 

 

  • 22
    点赞
  • 97
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值