直接使用提供的代碼架構進行修改,是最快得到效果的方法;但是這樣的靈活性較差,而且真正的程式員從來都不會停滞在這一步:我們需要的是“将架構解析到最小化、理清楚每個建構之間的關系”,隻有這樣才能靈活運用。
一、準備工作1、高拍儀已經接通,如果需要的話,還要安裝驅動;
2、vs2012程式設計環境,能夠編寫Csharp和OpenCV程式(具體不清楚可以回過頭來看配置);
3、

是DirectShow.net(http://directshownet.sourceforge.net/docs.html)的可使用類
它本身包含文檔,有時間可以看一下。最新更新時間2010年。
4、
是由
生成的。這裡可以先立足修改已經編輯成功的項目(具體原理将在下一課講解)
二、配置程式1、添加引用
directshow.net的話,直接引用dll就可以了
2、拖動控件
使用Csharp編寫界面,可以重複使用
的定位功能
以及Dock的停靠功能
圖像顯示的地方,肯定需要的是picturebox,不妨連同lena一起拷貝過來
由于采集處理是一個實時過程,我們采用timer控件來控制(關于是使用timer還是開線程,那種比較好,我們在架構融合的時候專門比較,并選擇)
性Interval采用50即可,以為50*24>1000,一般來說還是有認為24幀以上比較連貫。
三、編寫代碼1、添加頭檔案和引用,并添加capture.cs
其中,Capture是一個專門對Directshow的采集裝置的封裝,裡面有豐富的功能;是官方提供的代碼,可以較為方向應用。
注意修改命名空間
2、編寫選擇視訊準備函數(注意這裡預設裝置為640*480),并且我在選擇的時候預設選擇了第2個(序号為1)的裝置,因為我用的是筆記本,有内置攝像頭
相關函數的操作,注意參考相關注釋
//選擇視訊裝置
public
void
InitVideoDevice()
{
try
{
if
(cam != null)
cam.Dispose();
//讀取參數
int
VIDEODEVICE = 1; // zero based index of video capture device to use
const
int
VIDEOWIDTH = 640;// 是用預設(最大)分辨率
const
int
VIDEOHEIGHT = 480; // Depends on video device caps
const
int
VIDEOBITSPERPIXEL = 24; // BitsPerPixel values determined by device
cam =
new
Capture(VIDEODEVICE, VIDEOWIDTH, VIDEOHEIGHT, VIDEOBITSPERPIXEL, picPreview );
}
catch
{
MessageBox.Show("攝像頭打開錯誤,請首先確定攝像頭連接配接并至少支援1024*768分辨率!");
}
}
并且在form的init中進行調用,確定不能夠出錯
public
FormMain()
{
InitializeComponent();
//構造攝像頭資料
foreach (DsDevice ds in DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice))
{
cbCam.Items.Add(ds.Name);
}
//初始化攝像頭
InitVideoDevice();
}
此時,已經可以預覽,并且獲得所有視訊裝置
4、編寫timer事件
為了将OpenCV的函數融入進去,必須自己編寫timer事件。在這個timer事件中,最重要的操作就是
讀取directshow.net産生的結果
調用OpenCV的函數進行處理
将處理的結果回報到directshow.net中去顯示出來
private
void
timer_Tick(object sender, EventArgs e)
{
// Release any previous buffer
if
(m_ip != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(m_ip);
m_ip = IntPtr.Zero;
}
// capture image
try
{
m_ip = cam.Click();
}
catch
{
//do nothing,允許丢幀 TODO:是否改成繼承上一幀更好
}
Bitmap b =
new
Bitmap(cam.Width, cam.Height, cam.Stride, PixelFormat.Format24bppRgb, m_ip);
// If the image is upsidedown
b.RotateFlip(RotateFlipType.RotateNoneFlipY);
srcImage = b;
if
(picPreview.Image != null)
picPreview.Image.Dispose();
//調用clr+opencv圖像處理子產品
MemoryStream ms =
new
MemoryStream();
b.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] bytes = ms.GetBuffer();
Bitmap bitmap = client.testMethod(bytes);
//顯示結果
picPreview.Image = bitmap;
}
這段代碼比較關鍵
// Release any previous buffer
if
(m_ip != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(m_ip);
m_ip = IntPtr.Zero;
}
首先判斷指針是否為空
// capture image
try
{
m_ip = cam.Click();
}
catch
{
//do nothing,允許丢幀 TODO:是否改成繼承上一幀更好
}
Bitmap b =
new
Bitmap(cam.Width, cam.Height, cam.Stride, PixelFormat.Format24bppRgb, m_ip);
// If the image is upsidedown
b.RotateFlip(RotateFlipType.RotateNoneFlipY);
srcImage = b;
而後通過調用Click()獲得目前視訊資料,并且将其轉換為BitMap格式
if
(picPreview.Image != null)
picPreview.Image.Dispose();
//調用clr+opencv圖像處理子產品
MemoryStream ms =
new
MemoryStream();
b.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] bytes = ms.GetBuffer();
Bitmap bitmap = client.testMethod(bytes);
最為關鍵的調用client.testMethod方法來進行圖像處理,并将結果直接轉換為bitmap,最後這個結果顯示在另外一個picturebox上面。
如果你去看這個testMethod,是一個非常典型的“三明治”結構:包括将<byte>結構轉換為Mat,調用OpenCV函數對Mat進行處理,将Mat結果傳回出來。
Bitmap^ GOClrClass::testMethod(cli::array<
unsigned
char
>^ pCBuf1)
{
////////////////////////////////将輸入cli::array<unsigned char>轉換為cv::Mat/////////////////////////
pin_ptr<System::Byte> p1 = &pCBuf1[0];
unsigned
char
* pby1 = p1;
cv::Mat img_data1(pCBuf1->Length,1,CV_8U,pby1);
cv::Mat img_object = cv::imdecode(img_data1,IMREAD_UNCHANGED);
if
(!img_object.data)
return
nullptr;
////////////////////////////////////////////OpenCV的算法處理過程////////////////////////////////////
cvtColor(img_object,img_object,COLOR_BGR2GRAY);
cvtColor(img_object,img_object,COLOR_GRAY2BGR);
Mat drawing = img_object.clone();
/////////////////////////将cv::Mat轉換為Bitmap(隻能傳輸cv_8u3格式資料)///////////////////////////////
if
(!drawing.data)
return
nullptr;
Bitmap^ bitmap = MatToBitmap(drawing);
return
bitmap;
}
而主要算法,隻是一個灰階處理。(我首先将彩色圖像轉換為灰階,然後再将灰階轉換為彩色,是為了保持3通道)
四、測試結果為了顯示結果,我添加了一個picResult的picturebox,則調用OpenCV的處理結果顯示在右側。
。
到此為止,基于GOCW,建立一個實時視訊程式的基本流程已經明晰了,如果感興趣,相關的原理請關注後續部落格。
感謝閱讀至此,
希望有所幫助。
P.S小技巧:在tab選擇到預覽視窗的時候,才打開timer,這樣能夠保證最好效率。
private
void
tabControl_SelectedIndexChanged(object sender, EventArgs e)
{
if
(tabControl.SelectedIndex == 1)//隻有在預覽的時候打開圖像處理
timer.Enabled =
true
;
else
timer.Enabled =
false
;
}
來自為知筆記(Wiz) 目前方向:圖像拼接融合、圖像識别
聯系方式:[email protected]