用OpenCV來擷取Webcam的畫面 - C語言

Webcam現在很常見,可以用來視訊對話、拍照、錄影等等,近期的筆記型電腦都有內建了,而市面上賣的獨立USB攝影機甚至可以拍攝HD畫質呢。除了比較一般的用途,我們還可以利用Webcam把畫面加入程式裡。這次是自己動手寫程式,利用OpenCV的函式庫來擷取攝影機(Webcam)的畫面,可以從螢幕上看到Webcam的內容,也可以根據個人需求變成一個自動拍照的攝影機。

軟體部分是用C語言加入OpenCV函式庫,硬體部分就是筆電上的內建攝影機而已(夠簡單吧)。軟體編譯的是在Ubuntu下使用gcc編譯。



檔案編譯

使用gcc做編譯時記得要加上後面這些東西,否則無法鏈結到OpenCV的函式。編譯格式如下:
    $ gcc -o <檔名> <檔名.c>  `pkg-config --cflags opencv` `pkg-config --libs opencv` -ggdb

範例 (範例C語言檔案為camDis.c):
    $ gcc -o camDis camDis.c  `pkg-config --cflags opencv` `pkg-config --libs opencv` -ggdb


編譯完之後可以試著直接執行檔案,在檔案執行的時候應該會產生以下信息,他會在開啟Webcam的時候出現,但用過一段時間後我是覺得不影響主要功能,所以就沒有去處理他了,就它當成正常狀況。
    VIDIOC_QUERYMENU: Invalid argument

固定時間擷取影像

如果想要讓自己的Webcam變成一個自動照相機,以下這個方式可以讓Webcam只在特定的時間拍照並存檔。因為只有在拍照的過程中才會啟動攝影機功能,所以不用擔心持續啟動攝影機會造成系統的負擔,節省系統效能。

在這個程式裡所設定的工作是每5秒時截取一次圖像,所以每一次存好圖片後會使用cvWaitKey進行休眠,而這個函數可以偵測鍵盤的回傳值,當鍵盤中輸入esc時,會跳出while迴圈,程式就回結束。

#include "opencv2/highgui/highgui.hpp"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>


int main(int argc, char** argv)
{
   //image header
   IplImage* image;
   int i;

   CvCapture* capture = 0;
   
   cvNamedWindow( "IMAGE", 1 );
       
   while(1)
   {
       capture = cvCaptureFromCAM(0);
       if( capture )
       {
//設定擷取畫面大小
           cvSetCaptureProperty(
                     capture,CV_CAP_PROP_FRAME_WIDTH,320);
           cvSetCaptureProperty(
                     capture,CV_CAP_PROP_FRAME_HEIGHT,240);
           
image = cvQueryFrame( capture );
 
//擷取畫面前先將攝影機跑過一陣子
           for(i=0;i<15;i++)
           {
               if(image = cvQueryFrame( capture ))
               {
                   cvWaitKey(20);
               }
               else
               {
                   break;
               }
           }

           cvShowImage( "IMAGE", image );
           cvSaveImage("./pic1.jpeg",image,0);
           cvReleaseCapture( &capture );

//這裡cvWaitKey設定5秒,代表攝影機5秒才會截圖一次
//可依據需求做調整
           if(cvWaitKey(5000)==27)  //press "esc" to exit
           {
               break;
           }            
       }
       else
       {
           cvWaitKey(30);
           continue;
       }
       
   }
   cvDestroyWindow( "IMAGE" );
   return 0;
}



首先,在程式碼中,以下三者是一組的程式碼依序是,打開視窗、把圖片顯示到視窗中、關閉視窗:
cvNamedWindow( "IMAGE", 1 );
cvShowImage( "IMAGE", image );
cvDestroyWindow( "IMAGE" );

變數capture在一開始被宣告為CvCapture*型態,利用cvCaptureFromCAM(0)可以指定要從Webcam中擷取畫面,0是預設代號。

image = cvQueryFrame( capture )是把image指向擷取到的影像。

cvReleaseCapture可以釋放capture指標,在結束攝影機工作時記得要釋放掉

cvSaveImage函數可以存放影像,影像的路徑可以打絕對路徑或者相對路徑。

cvSetCaptureProperty的部分可以用來設定擷取的型態,在這邊是設定擷取的圖片大小為320*240,當然也可以設定為640*480,這是因人而異的。


之所以要先跑一個for迴圈在抓圖片,是因為我的攝影機在啟動時畫面會從全黑慢慢轉變成一般亮度,如果我只抓了第一張,那畫面就是一片黑...(可能是筆電上的Webcam太廉價)

執行的畫面:







連續擷取

以下是連續執行攝影機,當cvWaitKey偵測到esc按鍵之後,就會跳出while迴圈,存下最後一張圖片後結束。

雖然是連續截圖,但是在每一次截圖的中間可以擺上一個10~30ms的cvWaitKey等待時間,因為webcam的最高速度通常都是每秒30張左右,所以中間停留一陣子是不影響的。

程式的架構與上一個範例相似,差別在於while擺放的位置是不同的,所以程式的執行目的也跟著改變。


#include "opencv2/highgui/highgui.hpp"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>


int main(int argc, char** argv)
{
   //image header
   IplImage* image;


   CvCapture* capture = 0;
   
   capture = cvCaptureFromCAM(0);
   cvSetCaptureProperty(
capture,CV_CAP_PROP_FRAME_WIDTH,320);
   cvSetCaptureProperty(
capture,CV_CAP_PROP_FRAME_HEIGHT,240);
   
if( capture )
   {
       cvNamedWindow( "IMAGE", 1 );
       while(1)
       {
           image = cvQueryFrame( capture );
           
           cvShowImage( "IMAGE", image );
           if(cvWaitKey(30)==27)  //press "esc" to exit
           {
               break;
           }
       }        
       cvSaveImage("./pic2.jpeg",image,0);
       cvDestroyWindow( "IMAGE" );
   }
   cvReleaseCapture( &capture );
   return 0;
}


執行結果:




最後,所擷取到的圖片除儲存下來之外,還可以進行各種的影像處理,只需要把image所截到的圖片進入運算處裡即可。



留言

  1. 非常高興看到這篇的分享, 您有email 資訊嗎 ? 想請教您opencv一些問題 ?
    感恩

    回覆刪除
    回覆
    1. 您可以試著描述一下您的問題,我可以試試。最近工作忙碌,可能不會太常上來。

      刪除
  2. 謝謝您的回覆,剛好工作上有個外型邊緣要檢測,想問您是否有此相同的經驗 ? 請教!

    回覆刪除
  3. 哈囉,我想問你一下,我想利用攝影機擷取到的圖片進行光流法的運算,然而光流法是運用連續的兩張圖片來進行運算,我想請教的是,程式碼該怎麼寫才能達到取得連續兩張的圖片?
    謝謝。

    回覆刪除
  4. 請問我用樹梅派的PYTHON 可以用嗎? 我也是要用跟你很像的縮時攝影,但不知道能不能用

    回覆刪除
    回覆
    1. 應該是可以的,但是程式語言不同,呼叫opencv的函式方式可以參考opencv裏頭的所附閱的python程式範例。
      要注意的是Raspberry PI的效能不及一般的個人電腦,在使用攝影機裝置時,要考慮一下計算的時間與截圖取樣的頻率(時間間隔)。

      刪除
  5. 我想問一個有關cvSaveImage的問題
    能夠存成不同的檔名嗎?
    像是001 002 003 ... XXX
    不要重複在同個檔名上
    我已經找過很多相關OPENCV資料
    能不能有個範例 讓我參考..

    回覆刪除
    回覆
    1. 這應該跟OpenCV本身比較沒有關係了
      或許可以在cvSaveImage將檔案名稱中加入一個計數器,做字串處理

      刪除
  6. 想請問假如我是不是用電腦的鏡頭,而是用USB外接鏡頭那要如何改寫程式呢?(opencv新手)

    回覆刪除
    回覆
    1. 試試修改這個函數cvCaptureFromCAM(0)裏頭的參數

      刪除

張貼留言

這個網誌中的熱門文章

[比較]華碩ASUS ZenFone 3系列的各種變形分析。買手機、挑手機的攻略

[開箱]索尼粉必備。美麗窄邊框手機Sony Xperia XA1 粉嫩嫩開箱與使用心得

在Linux上常用的終端機指令(shell)整理

[比較]Arduino各開發板之差異比較

[開箱]震撼的神器VR CASE 5 PLUS開箱,便宜簡單又快速的手機頭戴式VR眼鏡

[台東]騎機車遊台東,三仙台、池上便當、伯朗大道金城武樹一日走透透