1. 程式人生 > >LabWindows/CVI之無驅攝像頭使用--例項:遠端監控(TCP)

LabWindows/CVI之無驅攝像頭使用--例項:遠端監控(TCP)

緣由:作為一枚還沒出學校的實習僧,進入公司接手的第一個專案,沒人帶,沒用過CVI,而且公司還沒人做過,幸好作為一個現代人還有網際網路。從一無所知接手到寫完這個測試程式,接近花了我3周左右的時間。我認為很有意義,實際專案要比這複雜,這只是其中一個功能而已。

其中兩人的部落格的資料對我來說較為重要,影響著整個程式設計。
1. 小信:基於LabWindows/CVI的攝像頭控制技術--實現螢幕截圖、錄影功能 部落格源地址:http://blog.sina.com.cn/s/blog_4b677075010006da.html
2. 醉囧裡囧夢囧回:LabWindows/CVI入門之第六章:綜合例項:遠端監控系統 部落格源地址:

http://blog.sina.com.cn/s/blog_6373e9e60101cetk.html
(我有部分是借鑑醉囧裡囧夢囧回的程式設計思路,但有不一樣,他所用的是通過網頁實現影象顯示,而我是通過TCP服務端和客戶端實現影象顯示)

一、任務描述:
1.影象採集(在CVI攝像頭控制技術裡有較為詳細的介紹)
2.影象傳輸(將影象資料通過Base64轉碼,將Base64編碼通過TCP有客戶端傳送給服務端)
3.影象接收並顯示(將接收資料,通過Base64解碼,轉換為影象資料儲存顯示)
注:因為我在查詢圖片轉發時,最開始查詢到的方法是直接將圖片轉換為二進位制資料傳送就行,但實際操作中資料轉發和接收到的資料轉成圖片時,圖片不能正常生成。所以最好選擇base64轉碼解決這個問題。
介面效果:
遠端監控介面效果


這裡寫圖片描述

二、系統詳細設計:
(1)影象採集
我所用USB攝像頭為免驅攝像頭,“免驅”的意思並不是說這種攝像頭不需要驅動即可執行,而是因為目前市面上主流的作業系統中已經包含了該攝像頭所需要的驅動程式,當攝像頭連線計算機後無需額外安裝驅動程式。
考慮到普通攝像頭載入驅動時均載入avicap32.dll,而且使用AVICAP32開發複雜度較低,所以就才用AVICAP32進行開發。需要在程式中引入avicap32的動態連結庫。
UI設計:
這裡寫圖片描述
1)攝像頭初始化開啟:

     int Display_width;
     int Display_height;
     int
Display_top; int Display_left; int i; //獲得面板控制代碼,用於在呼叫攝像頭函式時,需要使用的控制代碼handle GetPanelAttribute (panelHandle, ATTR_SYSTEM_WINDOW_HANDLE, &handle); //獲得螢幕中影象要顯示的位置 GetCtrlAttribute (panelHandle, PANEL_CANVAS_DISPLAY, ATTR_LEFT, &Display_left); GetCtrlAttribute (panelHandle, PANEL_CANVAS_DISPLAY, ATTR_TOP, &Display_top); GetCtrlAttribute (panelHandle, PANEL_CANVAS_DISPLAY, ATTR_HEIGHT, &Display_height); GetCtrlAttribute (panelHandle, PANEL_CANVAS_DISPLAY, ATTR_WIDTH, &Display_width); //開啟攝像頭 result = capCreateCaptureWindowA("",WS_CHILD | WS_VISIBLE ,Display_left,Display_top,Display_width,Display_height,handle,0); //設定攝像頭函式 SendMessage((HWND)result, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0, 0); SendMessage((HWND)result, WM_CAP_SET_CALLBACK_ERROR, 0, 0); SendMessage((HWND)result, WM_CAP_SET_CALLBACK_STATUSA, 0, 0); for(i=0;i<3;i++) { SendMessage((HWND)result, WM_CAP_DRIVER_CONNECT, 0, 0); } SendMessage((HWND)result, WM_CAP_SET_SCALE, 1, 0); SendMessage((HWND)result, WM_CAP_SET_PREVIEWRATE, 66, 0); SendMessage((HWND)result, WM_CAP_SET_OVERLAY, 1, 0); SendMessage((HWND)result, WM_CAP_SET_PREVIEW, 1, 0);

注:在開啟攝像頭中出現過攝像頭指示燈亮,但介面上一片漆黑,沒有影象。原因為驅動沒有呼叫。多呼叫幾次影象就會出現。也就是原始碼中的:SendMessage((HWND)result, WM_CAP_DRIVER_CONNECT, 0, 0);

2)截圖操作

int CVICALLBACK CapOneSnap (int handle, int result, int message, unsigned int* wParam, unsigned int* lParam, void* callbackData)
{
    char bmpFilePath[MAX_PATHNAME_LEN ]="";
    char jpgFilePath[MAX_PATHNAME_LEN ]="";
    int bitmap_ID;
    switch(message)
    {
        case (WM_USER+1009):
            //do some things.
            GetDir(bmpFilePath);
            GetDir(jpgFilePath);     
            sprintf(bmpFilePath,"%s\\%u.bmp",bmpFilePath,(unsigned long)(*wParam));
            sprintf(jpgFilePath,"%s\\%u.jpg",jpgFilePath,(unsigned long)(*wParam));

            SendMessage((HWND)result,WM_CAP_SAVEDIB,0,(LPARAM)bmpFilePath);
            GetBitmapFromFile ( bmpFilePath , &bitmap_ID );
            SaveBitmapToJPEGFile(bitmap_ID,jpgFilePath,JPEG_DCTFAST,90);
            DiscardBitmap(bitmap_ID);
            DeleteFile(bmpFilePath);
            break;
        case EVENT_NEWHANDLE :
            handle=*wParam;
            break;
    }
    return 0;
}

注:功能是儲存螢幕影象資料為一張點陣圖,這是CVI自帶的函式,然後將點陣圖儲存為jpg圖片(jpg是所知壓縮率最大的圖片格式,大約為一張1M左右的點陣圖轉換為50K左右的jpg圖片)並刪除點陣圖。返回圖片編號wParam。

3)將jpg圖片轉換為base64編碼

int GetCap(int panel,int result, float* eclipsTime,unsigned char* base64Content)
{
    double startTime;
    char jpgFIlePath[MAX_PATHNAME_LEN]="";
    unsigned int randNum=GetRandNumber();
    unsigned char* fileContent;
    int fileLength;
    FILE* fp;
    startTime=Timer();
    CapOneSnap (panel,result, WM_USER+1009, &randNum , 0,NULL);
    GetDir(jpgFIlePath);
    sprintf(jpgFIlePath,"%s\\%u.jpg",jpgFIlePath,(unsigned int)(randNum));
    if((fp = fopen(jpgFIlePath,"rb")) == NULL)
    {
        *eclipsTime=1000*(Timer()-startTime);
        return -1;
    }
    fileContent=(unsigned char*)malloc(921654);
    fileLength=fread(fileContent,1,921654,fp);
    fclose(fp);
    DeleteFile(jpgFIlePath);
    if(fileLength==0)
    {
        *eclipsTime=1000*(Timer()-startTime);
        return -2;
    }
    fnBase64Encode(fileContent,base64Content,fileLength);
    free(fileContent);
    *eclipsTime=1000*(Timer()-startTime);
    return 0;
}
//圖片進行base64編碼
//返回base64編碼值
const char BASE_CODE[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int fnBase64Encode(unsigned char *lpString,unsigned char *lpBuffer, int sLen)   
{ 
    int vLen = 0;    
    while(sLen > 0)        
    {   
        *lpBuffer++ = BASE_CODE[(lpString[0] >> 2 ) & 0x3F];  
        if(sLen > 2)  
        {   
            *lpBuffer++ = BASE_CODE[((lpString[0] & 3) << 4) | (lpString[1] >> 4)];  
            *lpBuffer++ = BASE_CODE[((lpString[1] & 0xF) << 2) | (lpString[2] >> 6)];  
            *lpBuffer++ = BASE_CODE[lpString[2] & 0x3F];  
        }
        else  
        {   
            switch(sLen)    
            {   
            case 1:  
                *lpBuffer ++ = BASE_CODE[(lpString[0] & 3) << 4 ];  
                *lpBuffer ++ = '=';  
                *lpBuffer ++ = '=';  
                break;  
            case 2:  
                *lpBuffer ++ = BASE_CODE[((lpString[0] & 3) << 4) | (lpString[1] >> 4)];  
                *lpBuffer ++ = BASE_CODE[((lpString[1] & 0x0F) << 2) | (lpString[2] >> 6)];  
                *lpBuffer ++ = '=';  
                break;  
            }  
        }  
        lpString += 3;  
        sLen -= 3;  
        vLen +=4;  
    }  
    //*lpBuffer = 0; 
    *lpBuffer = '\0';
    return vLen;  
}

注:圖片與base64編碼之間的相互轉換,在網上有很多例項。我這裡就不具體介紹,自己去了解。程式碼中還有一個算延遲時間的(eclipsTime),我這裡沒用,但保留了這個。

(2)影象傳輸:
我這裡是影象採集這邊是客戶端,影象接收端是伺服器端。
客戶端:

int TcpFilishedOk=0;
int CVICALLBACK ClientCallback(unsigned int handle,int event,int error,void* callbackData)
{
    char ss[ONE_POCKET_SIZE+1];
    int rdSize=0;
    unsigned char* base64Content;
    float eclipsTime=0;
    switch(event)
    {
        case TCP_DATAREADY:    //收到資料
            rdSize=ClientTCPRead(conversationHandle,ss,ONE_POCKET_SIZE,2000);
            ss[rdSize]=0;
            if(strcmp(ss,"CAPTURE") == 0)
            {  
                ClientTCPWrite(handle,"image=",7,2000);
                TcpFilishedOk=0;
            }
            if(strcmp(ss,"askok") == 0)
            {
                base64Content=(unsigned char*)malloc(999999);
                if(GetCap(panelHandle,result,&eclipsTime,base64Content)>=0)
                {
                     //傳送圖片base64編碼
                  if(ClientTCPWrite(handle,base64Content,strlen(base64Content),20000)<0)
                     {   
                        MessagePopup("提示","Write imageInfo  failed.");
                     }
                  else
                  {
                      TcpFilishedOk=1;
                  }
                }
                else
                {
                    MessagePopup("提示","Open JPGFile failed.");
                    sprintf(ss,"({\n\"errorCode\":1,\n\"eclipsTime\":%4.2f,\n\"image\":null\n})",eclipsTime);
                    if(ClientTCPWrite(handle,ss,strlen(ss),2000)<0)
                      {   
                         MessagePopup("提示","Write image NULL failed.");
                      }
                 }
                 //釋放base64Content分配空間
                 free(base64Content);
                //清空ClientTCPRead()中緩衝區buffer的資料防止出錯        
                while(rdSize>=ONE_POCKET_SIZE)//把沒讀出來的資料都讀出來防止再次DATAREADY事件
               {
                    rdSize=ClientTCPRead(handle,ss,ONE_POCKET_SIZE,2000);
               }
                //DisconnectTCPClient (handle); 
            }
            if(TcpFilishedOk)
            {
                //ClientTCPWrite(handle,"結束",5,2000);
                TcpFilishedOk=0;
            }
            break;
        case TCP_DISCONNECT:   //斷開連線
            //ShowState("Connection disconnected!");
            sprintf(ss,"Connection disconnected!");
            MessagePopup("提示",ss);
            break;
    }
    return 0;
}

伺服器端:

int CVICALLBACK ServerCallback(unsigned int handle,int event, int error,void* callbackData)
{
    char ss[ONE_POCKET_SIZE+1];
    int rdSize=0;
    unsigned char* base64Content;
    float eclipsTime=0;
    switch(event)
    {
        case TCP_CONNECT:    //建立連線
            //
            convHandle=handle;
            sprintf(ss,"Connection %d established!",handle);
            MessagePopup("提示",ss);
            break;
        case TCP_DISCONNECT:
            //
            sprintf(ss,"Connection %d disconnected!",handle);
            MessagePopup("提示",ss);
            break;
        case TCP_DATAREADY:  //收到資料
            if(camera_ok)
            {
                CAMERA();
            }
            if(monitor_ok)
            {
                MONITOR();
            }
            break;
    }
    return 0;
}

注:在我的程式中所謂的監控就是在有效的時間內多次採集圖片資料並在遠端端顯示,遠端監控在沒有足夠的網速和高效的影象壓縮演算法的條件下,實現起來是不現實的,我嘗試過傳送流媒體,無論是CVI自身儲存的視訊(CVI儲存的流媒體太大,二三十秒資料,大約佔據了我1G的儲存位置)還是我們下載下來的“清晰”視訊,效率太低。或者直接無法傳輸。我這邊暫時還沒有解決。

(3)影象接收並顯示

//攝影
int CAMERA (void)
{
    static char ss[ONE_POCKET_SIZE];
    int rdSize=0;
            if((rdSize=ServerTCPRead(convHandle,ss,ONE_POCKET_SIZE,2000))<0)
                printf("Acquire data failed!\n");
            else
            {   

                if(strcmp(ss,"image=") == 0)
                {
                     ServerTCPWrite(convHandle,"askok",6,2000);         
                }
                else
                {
                    ShowState("圖片資料接收中。。");
                     if(Decode(ss,"base64.jpg"))
                        { 
                            Delay(0.5);
                            //顯示圖片到PICTURE控制元件上
DisplayImageFile(panelHandle,PANEL_PICTURE,"base64.jpg");
                            //ShowState("圖片正在顯示。。。。。");
                            TM_OK_FLAG=1;
                        }
                }
            }
            return 1;

}

//監控
int threadID[26];
int MONITOR(void)
{
    static char ss[ONE_POCKET_SIZE];
    int rdSize=0;
    if(monitor_ok)
    {
            if((rdSize=ServerTCPRead(convHandle,ss,ONE_POCKET_SIZE,2000))<0)
                printf("Acquire data failed!\n");
            else
            {   

                if(strcmp(ss,"image=") == 0)
                {
                     ServerTCPWrite(convHandle,"askok",6,2000);
                    // 傳送允許:傳送base64編碼
                }
                else
                {

                    if(MonitorCount == 26)
                    {
                        MonitorCount = 0;
                    }
                    Picture[MonitorCount].PictureFileName = PFile[MonitorCount].filename;
                    Picture[MonitorCount].PicturcBase64Data = ss;

                    PFile[MonitorCount].pfid = MonitorCount;
                        //建立一個新程函式接收圖片資訊並轉碼儲存

                    CmtScheduleThreadPoolFunction (DEFAULT_THREAD_POOL_HANDLE, ThreadFunction, MonitorCount, &threadID[MonitorCount]);
                        //傳送監控資訊
                        Delay(0.55);
                    ServerTCPWrite(convHandle,"CAPTURE",8,2000);
                    MonitorCount++;

                }
            }
            return 1;
    }   
    return 1;
}

//執行緒:解碼圖片資料並顯示
int CVICALLBACK ThreadFunction (int filenum)
{
        if(Decode(Picture[filenum].PicturcBase64Data,PFile[filenum].filename))
        {   
        //  ShowState("圖片正在顯示。。。。。");
            Delay(0.55); 
            DisplayImageFile(panelHandle,PANEL_PICTURE,PFile[filenum].filename);
            TM_OK_FLAG=1;
        }
    return 0;
}

base64編碼解碼成影象資料並儲存:

int Decode(char *base64,char *filename)
{
     char *bindata;
     FILE *fp;

    fp = fopen(filename,"wb");
    if(fp == 0)
    {
        //file is not
    }
    else
    {
        //ShowState("圖片資料接收結束");
        bindata=(char*)malloc(999999);
        int bytes;
        bytes = base64_decode( base64, bindata );

        //ShowState("圖片資料base64轉碼結束");
        fwrite(bindata,bytes,1,fp);
        free(bindata);
        fclose(fp);


    }
    return 1;
}   
//base64編碼字元
const char * base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

int base64_decode( const char * base64,char * bindata )
{
    int i, j;
    unsigned char k;
    unsigned char temp[4];
    for ( i = 0, j = 0; base64[i] != '\0' ; i += 4 )
    {
        memset( temp, 0xFF, sizeof(temp) );
        for ( k = 0 ; k < 64 ; k ++ )
        {
            if ( base64char[k] == base64[i] )
                temp[0]= k;
        }
        for ( k = 0 ; k < 64 ; k ++ )
        {
            if ( base64char[k] == base64[i+1] )
                temp[1]= k;
        }
        for ( k = 0 ; k < 64 ; k ++ )
        {
            if ( base64char[k] == base64[i+2] )
                temp[2]= k;
        }
        for ( k = 0 ; k < 64 ; k ++ )
        {
            if ( base64char[k] == base64[i+3] )
                temp[3]= k;
        }

        bindata[j++] = ((unsigned char)(((unsigned char)(temp[0] << 2))&0xFC)) |
                ((unsigned char)((unsigned char)(temp[1]>>4)&0x03));
        if ( base64[i+2] == '=' )
            break;

        bindata[j++] = ((unsigned char)(((unsigned char)(temp[1] << 4))&0xF0)) |
                ((unsigned char)((unsigned char)(temp[2]>>2)&0x0F));
        if ( base64[i+3] == '=' )
            break;

        bindata[j++] = ((unsigned char)(((unsigned char)(temp[2] << 6))&0xF0)) |
                ((unsigned char)(temp[3]&0x3F));
    }
    return j;
}

注:圖片資料必須寫入完成後,方能顯示在螢幕上,否則影象顯示將顯示一半或者直接花屏,但檔案寫入速度遠不如讀取速度,我用程式大概計算過,一張圖片從寫入到能顯示大約為0.45s左右。這也是我為什麼要在監控回撥函式中開執行緒的原因,希望能在短時間內儘量顯示多的圖片同時又要求影象儲存完整。
原始碼下載地址:http://download.csdn.net/detail/cb_869145753_hp/9736896

注意:我在程式中好像將客戶端和伺服器端相互調換過。但不影響程式和閱讀

相關推薦

LabWindows/CVI攝像頭使用--例項遠端監控TCP

緣由:作為一枚還沒出學校的實習僧,進入公司接手的第一個專案,沒人帶,沒用過CVI,而且公司還沒人做過,幸好作為一個現代人還有網際網路。從一無所知接手到寫完這個測試程式,接近花了我3周左右的時間。我認為很有意義,實際專案要比這複雜,這只是其中一個功能而已。 其中

資料結構實驗棧與佇列十走迷宮DFS

Problem Description 一個由n * m 個格子組成的迷宮,起點是(1, 1), 終點是(n, m),每次可以向上下左右四個方向任意走一步,並且有些格子是不能走動,求從起點到終點經過每個格子至多一次的走法數。 Input        第一行一個整數T

經驗總結-完整介紹Android Studio中Git的使用在GitHub上建立一個遠端倉庫

說完本地Git倉庫,那麼如何將專案上傳至遠端的GitHub倉庫呢?首先我們需要有一個託管平臺,然後需要建立一個倉庫。現在我們開始註冊一個GitHub賬號,然後去後new 一個倉庫吧: 一、首先我們需

演算法樂趣窮舉搜尋例項Google方程式;

如題:有一個有字元組成的等式:WWWDOT-GOOGLE = DOTCOM,每個字元代表一個0-9之間的數字,WWWDOT、GOOGLE和DOTCOM都是合法的數字,不能以0開頭,請找出一組字元和數字的對應關係,使得它們互相轉換,並且替換後的數字都能滿足等式。 總共可能性:

【SSH旅】一步步學習Hibernate框架關於持久化

stc localhost 對象 schema hbm.xml java let pass [] 在不引用不論什麽框架下,我們會通過平庸的代碼不停的對數據庫進行操作,產生了非常多冗余的可是又有規律的底層代碼,這樣頻繁的操作數據庫和大量的底層代碼的反復

python並發編程多進程(二)互斥鎖同步鎖&進程其他屬性&進程間通信queue&生產者消費者模型

互斥 數據 socket pan copy src too 如果 搶票 一,互斥鎖,同步鎖 進程之間數據不共享,但是共享同一套文件系統,所以訪問同一個文件,或同一個打印終端,是沒有問題的, 競爭帶來的結果就是錯亂,如何控制,就是加鎖處理 part1:多個進程共享同

ASP.NET Aries 高級開發教程Excel導入多表高級導入配置

lis 數據庫 名稱 教程 配置 rdquo net 列名 邏輯 前言: 在面對Excel的各種復雜導入情況中,多表導入是很常見的情景。 今天就來寫一下多表導入是如何配置的。 1、自定義導入模板 怎麽自定義: 其實就是自己新建一個Excel了,把列頭都寫好。

在Ubuntu上學習OpenStack網絡計算節點上網絡補充配置

分享 ini onf stack -a ubunt con openstac bubuko (註意:前面已經為計算+網絡節點配置了兩塊網卡eth0和eth1) v 執行如下命令: sudo ovs-vsctl add-br br-eth1 sudo ovs-vsctl ad

例項學習ansible系列5常用模組copy

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

面向物件【day07】類的例項化過程剖析

本節內容 1、概述 2、類的語法 3、總結 一、概述    之前我們說關於python中的類,都一臉懵逼,都想說,類這麼牛逼到底是什麼,什麼才是類?下面我們就來講講,什麼是類?它具有哪些特性。 二、類的語法 2.1 語法

資料結構實驗棧與佇列四括號匹配SDUT 2134

#include <bits/stdc++.h> using namespace std; typedef long long ll; char s[100]; char a[100]; int main() { int i,j,k,f,top,len; while(

TensorflowMNIST手寫數字識別分類問題1

一、MNIST資料集讀取 one hot 獨熱編碼獨熱編碼是一種稀疏向量,其中:一個向量設為1,其他元素均設為0.獨熱編碼常用於表示擁有有限個可能值的字串或識別符號優點:   1、將離散特徵的取值擴充套件到了歐式空間,離散特徵的某個取值就對應歐式空間的某個點    2、機器學習演算法中,

TensorflowMNIST手寫數字識別分類問題2

整體程式碼: #資料讀取 import tensorflow as tf import matplotlib.pyplot as plt import numpy as np from tensorflow.examples.tutorials.mnist import input_data mnis

Python十五網路程式設計

python基礎之網路程式設計(上篇)   socket程式設計   本篇介紹socket是基於什麼來的,為什麼要知道網際網路底層實現通訊的原理 一、客戶端/服務端架構 即C/S架構,包括 1.硬體C/S架構(印表機) 2.軟體C/S架構(web

資料結構實驗二叉樹七葉子問題SDUT 3346

#include <bits/stdc++.h> using namespace std; struct node { char data; struct node *lc, *rc; }; char a[100]; int num = 0; struct node

程式設計菜鳥到大佬計算機網路

計算機網路概述 計算機網路基本概念 什麼是計算機網路? 計算機網路=通訊技術+計算機技術,即計算機網路是通訊技術與計算機技術緊密結合的 產物。 計算機網路就是一種通訊網路: 定義:計算機網路就是互連的、 自治的計算機集合。

Pytorch第二課package-torch2 數學操作

微博:https://weibo.com/wangxiaocaoai/profile?rightmod=1&wvr=6&mod=personinfo 微信公眾號:搜尋"AI躁動街" 本節要點: 1 逐點計算操作 2 縮減操作 3 比較操作 4 其

Pytorch第一課package-torch1張量初識

微博:https://weibo.com/wangxiaocaoai/profile?rightmod=1&wvr=6&mod=personinfo 微信公眾號:搜尋"AI躁動街" 本節要點: 1 張量 2 建立張量的方式 3 張量的索引,切片,連線

Python極簡教程資料格式化format

自 python 2.6 開始,新增了一種格式化字串的函式str.format(),可謂威力十足。那麼,他跟之前的%型格式化字串相比,有什麼優越的存在呢?讓我們來揭開它羞答答的面紗。 #語法 它通過{}和:來代替%。 位置 '{0},{1}'.format('kzc',18) # k

Linux學習第二章配置網路IP,實現遠端連線

備註:屬於個人分享,文章如有問題請留言,謝謝! 第二章配置網路IP,實現遠端連線 1、輸入使用者和密碼 輸入密碼的時候是不會顯示的 如何檢視Linux系統是32位還是64位,X86是32位,X86_64是64位                  命令: unam