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

来源:互联网 发布:分班软件fenban 编辑:程序博客网 时间:2024/06/06 00:25

        博文由来笔者突发奇想,做采集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){//初始化WSAWORD 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[]){//初始化WSAWORD 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);//端口8888sin.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

 

 

 

 

 

 

 

0 0
原创粉丝点击