用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)裏頭的參數

      刪除
  7. 請問這樣的功能轉成PYTHON該怎麼寫呢?

    回覆刪除
    回覆
    1. Hi 您好
      應該是可以用python寫的,不過我沒有試寫過一樣功能python code.
      您可以參考opencv安裝套件內的sample code,找到與這段C語言中相對應的功能,就能夠以python實現。

      刪除

張貼留言

這個網誌中的熱門文章

[遊戲]手遊版RO仙境傳說正式上線,法師、巫師的練功心得與分享。RO守護永恆的愛

[比較]2017索尼XZ頂級旗艦手機選擇分析筆記。SONY XPERIA XZs、XZP、XZ1 Compact

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

[開箱]入手超小旗艦機Sony Xperia XZ1 Compact。期待已久的手機開箱時刻終於來了!

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

[心得]讓二支手機能夠同時擁有行動上網吃到飽。台灣之星4G自由配無痛申請

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