用OpenCV來擷取Webcam的畫面 - C語言
Webcam現在很常見,可以用來視訊對話、拍照、錄影等等,近期的筆記型電腦都有內建了,而市面上賣的獨立USB攝影機甚至可以拍攝HD畫質呢。除了比較一般的用途,我們還可以利用Webcam把畫面加入程式裡。這次是自己動手寫程式,利用OpenCV的函式庫來擷取攝影機(Webcam)的畫面,可以從螢幕上看到Webcam的內容,也可以根據個人需求變成一個自動拍照的攝影機。
軟體部分是用C語言加入OpenCV函式庫,硬體部分就是筆電上的內建攝影機而已(夠簡單吧)。軟體編譯的是在Ubuntu下使用gcc編譯。
$ 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
在這個程式裡所設定的工作是每5秒時截取一次圖像,所以每一次存好圖片後會使用cvWaitKey進行休眠,而這個函數可以偵測鍵盤的回傳值,當鍵盤中輸入esc時,會跳出while迴圈,程式就回結束。
首先,在程式碼中,以下三者是一組的程式碼依序是,打開視窗、把圖片顯示到視窗中、關閉視窗:
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太廉價)
執行的畫面:
雖然是連續截圖,但是在每一次截圖的中間可以擺上一個10~30ms的cvWaitKey等待時間,因為webcam的最高速度通常都是每秒30張左右,所以中間停留一陣子是不影響的。
程式的架構與上一個範例相似,差別在於while擺放的位置是不同的,所以程式的執行目的也跟著改變。
最後,所擷取到的圖片除儲存下來之外,還可以進行各種的影像處理,只需要把image所截到的圖片進入運算處裡即可。
軟體部分是用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所截到的圖片進入運算處裡即可。
非常高興看到這篇的分享, 您有email 資訊嗎 ? 想請教您opencv一些問題 ?
回覆刪除感恩
您可以試著描述一下您的問題,我可以試試。最近工作忙碌,可能不會太常上來。
刪除謝謝您的回覆,剛好工作上有個外型邊緣要檢測,想問您是否有此相同的經驗 ? 請教!
回覆刪除沒有耶,不好意思。
刪除哈囉,我想問你一下,我想利用攝影機擷取到的圖片進行光流法的運算,然而光流法是運用連續的兩張圖片來進行運算,我想請教的是,程式碼該怎麼寫才能達到取得連續兩張的圖片?
回覆刪除謝謝。
請問我用樹梅派的PYTHON 可以用嗎? 我也是要用跟你很像的縮時攝影,但不知道能不能用
回覆刪除應該是可以的,但是程式語言不同,呼叫opencv的函式方式可以參考opencv裏頭的所附閱的python程式範例。
刪除要注意的是Raspberry PI的效能不及一般的個人電腦,在使用攝影機裝置時,要考慮一下計算的時間與截圖取樣的頻率(時間間隔)。
我想問一個有關cvSaveImage的問題
回覆刪除能夠存成不同的檔名嗎?
像是001 002 003 ... XXX
不要重複在同個檔名上
我已經找過很多相關OPENCV資料
能不能有個範例 讓我參考..
這應該跟OpenCV本身比較沒有關係了
刪除或許可以在cvSaveImage將檔案名稱中加入一個計數器,做字串處理
想請問假如我是不是用電腦的鏡頭,而是用USB外接鏡頭那要如何改寫程式呢?(opencv新手)
回覆刪除試試修改這個函數cvCaptureFromCAM(0)裏頭的參數
刪除請問這樣的功能轉成PYTHON該怎麼寫呢?
回覆刪除Hi 您好
刪除應該是可以用python寫的,不過我沒有試寫過一樣功能python code.
您可以參考opencv安裝套件內的sample code,找到與這段C語言中相對應的功能,就能夠以python實現。