Python+OpenCV與海康工業(yè)相機(jī)交互
點(diǎn)擊下方卡片,關(guān)注“新機(jī)器視覺”公眾號(hào)
重磅干貨,第一時(shí)間送達(dá)
來源 | 點(diǎn)云PCL
1前言
因?yàn)轫?xiàng)目的原因,筆者需要開發(fā)自己的程序與海康工業(yè)相機(jī)(黑白相機(jī))進(jìn)行實(shí)時(shí)交互,而不是簡單的使用海康的驅(qū)動(dòng)來調(diào)用相機(jī)。
在經(jīng)歷了反復(fù)的踩坑填坑之后,筆者總結(jié)了利用c#和python與海康相機(jī)交互的方法。
2
準(zhǔn)備工作
無論是用c#還是python,都要通過海康的SDK來進(jìn)行二次開發(fā)。海康的SDK實(shí)際上就是一個(gè)第三方庫文件,里面有各種用于海康相機(jī)交互的函數(shù),這些函數(shù)被封裝成供各大編程語言使用的dll或者lib文件。你可以像使用OpenCV樣調(diào)用海康的SDK。
海康提供了各個(gè)編程語言的例程,可以讓我們對(duì)照例程快速上手。海康的例程一般放在相機(jī)驅(qū)動(dòng)軟件的安裝文件夾下,具體路徑為 …\MVS\Development\Samples
里面有大部分主流語言的例程:
除此之外,海康還提供了SDK的說明文檔,文件路徑在
…\MVS\Development\Documentations
c#可以參考.net版本的,python和c可以參考c版本的(兩者差別都不大)。
3
C#調(diào)用
在c#中的例程里面,有很多程序
但除了basicdemo有界面,其他的都是控制臺(tái)程序,顯示不了圖片。而且basicdemo基本涵蓋了海康相機(jī)使用的各個(gè)方面,所以推薦優(yōu)先學(xué)習(xí)basicdemo程序。
該程序的主界面如下圖所示:
需要注意的是,雖然這里能顯示圖片,但得到的圖片數(shù)據(jù)仍然是海康的圖片格式,不是opencv可以處理的mat格式。筆者嘗試了很多次,鑒于對(duì)C#不是很熟練,暫時(shí)無法將其轉(zhuǎn)為可供opencv處理的數(shù)據(jù)格式。這一功能我在python中實(shí)現(xiàn)了。
4
Python調(diào)用
python的話,參考例程中的grabimg程序,對(duì)里面的線程進(jìn)行改造即可。但這里也有一些問題需要注意
首先,官網(wǎng)給的源程序只能在控制臺(tái)顯示得到的圖片幀的長和寬,并沒有取得圖片的數(shù)據(jù)。但這明顯不是我們想要的。所以需要對(duì)其進(jìn)行改造,改造的部分主要在線程函數(shù)中
def work_thread(cam=0, pData=0, nDataSize=0):stOutFrame = MV_FRAME_OUT()memset(byref(stOutFrame), 0, sizeof(stOutFrame))while True:ret = cam.MV_CC_GetImageBuffer(stOutFrame, 1000)if None != stOutFrame.pBufAddr and 0 == ret:print ("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (stOutFrame.stFrameInfo.nWidth, stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nFrameNum))nRet = cam.MV_CC_FreeImageBuffer(stOutFrame)else:print ("no data[0x%x]" % ret)if g_bExit == True:break
這是官方的線程函數(shù)。我們需要在里面進(jìn)行改造。
改造之后的函數(shù)為
def work_thread2(cam=0, pData=0, nDataSize=0):# 輸出幀的信息stFrameInfo = MV_FRAME_OUT_INFO_EX()# void *memset(void *s, int ch, size_t n);# 函數(shù)解釋:將s中當(dāng)前位置后面的n個(gè)字節(jié) (typedef unsigned int size_t )用 ch 替換并返回 s# memset:作用是在一段內(nèi)存塊中填充某個(gè)給定的值,它是對(duì)較大的結(jié)構(gòu)體或數(shù)組進(jìn)行清零操作的一種最快方法# byref(n)返回的相當(dāng)于C的指針右值&n,本身沒有被分配空間# 此處相當(dāng)于將幀信息全部清空了memset(byref(stFrameInfo), 0, sizeof(stFrameInfo))while True:temp = np.asarray(pData) # 將c_ubyte_Array轉(zhuǎn)化成ndarray得到(3686400,)# print(temp)# print(temp.shape)temp = temp.reshape((2048, 1024, 3)) # 根據(jù)自己分辨率進(jìn)行轉(zhuǎn)化# temp = cv2.cvtColor(temp, cv2.COLOR_BGR2RGB) # 這一步獲取到的顏色不對(duì),因?yàn)槟J(rèn)是BRG,要轉(zhuǎn)化成RGB,顏色才正常gray = cv2.cvtColor(temp,cv2.COLOR_BGR2GRAY)ret,binary = cv2.threshold(gray,130,255,cv2.THRESH_BINARY)cv2.namedWindow("binary", cv2.WINDOW_NORMAL)cv2.namedWindow("ori", cv2.WINDOW_NORMAL)cv2.imshow('binary',binary)cv2.imshow("ori", temp)if cv2.waitKey(1) & 0xFF == ord('q'):break# 采用超時(shí)機(jī)制獲取一幀圖片,SDK內(nèi)部等待直到有數(shù)據(jù)時(shí)返回,成功返回0ret = cam.MV_CC_GetOneFrameTimeout(pData, nDataSize, stFrameInfo, 1000)if ret == 0:print("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (stFrameInfo.nWidth, stFrameInfo.nHeight, stFrameInfo.nFrameNum))else:print("no data[0x%x]" % ret)if g_bExit == True:breakcv2.waitKey()
除此之外,還有幾個(gè)點(diǎn)需要注意
第一個(gè)
sys.path.append(r"**\MvImport")因?yàn)樾枰玫絊DK的接口函數(shù),所以需要導(dǎo)入相應(yīng)的庫,官方的路徑是這樣的,這是因?yàn)檫@個(gè)包在程序的同一個(gè)文件夾下,所以前面的都可以省略,但我們使用的時(shí)候,最好把它的絕對(duì)路徑給寫上,我的路徑是這樣的,可以參考
sys.path.append(r"C:\Users\Administrator\Desktop\python_vscode\MvImport")
第二個(gè)
在線程函數(shù)之外,還有幾個(gè)關(guān)鍵語句需要注意
#ch:獲取數(shù)據(jù)包大小 | en:Get payload sizestParam = MVCC_INTVALUE()#csharp中沒有memset函數(shù),用什么代替?memset(byref(stParam), 0, sizeof(MVCC_INTVALUE))# MV_CC_GetIntValue,獲取Integer屬性值,handle [IN] 設(shè)備句柄# strKey [IN] 屬性鍵值,如獲取寬度信息則為"Width"# pIntValue [IN][OUT] 返回給調(diào)用者有關(guān)相機(jī)屬性結(jié)構(gòu)體指針# 得到圖片尺寸,這一句很關(guān)鍵# payloadsize,為流通道上的每個(gè)圖像傳輸?shù)淖畲笞止?jié)數(shù),相機(jī)的PayloadSize的典型值是(寬x高x像素大小),此時(shí)圖像沒有附加任何額外信息ret = cam.MV_CC_GetIntValue("PayloadSize", stParam)
cam.MV_CC_GetIntValue(“PayloadSize”, stParam)這一句中的PayloadSize是流通道上的每個(gè)圖像傳輸?shù)淖畲笞止?jié)數(shù),相機(jī)的PayloadSize的典型值是(寬x高x像素大小),這是一個(gè)很關(guān)鍵的步驟,官方例子中并沒有獲得這個(gè)值。
nPayloadSize = stParam.nCurValue# ch:開始取流 | en:Start grab imageret = cam.MV_CC_StartGrabbing()if ret != 0:print ("start grabbing fail! ret[0x%x]" % ret)sys.exit()# 關(guān)鍵句,官方?jīng)]有這個(gè)句子,抓取的圖片數(shù)據(jù)是空的,CSharp中怎么實(shí)現(xiàn)這句話。data_buf = (c_ubyte * nPayloadSize)()
data_buf = (c_ubyte * nPayloadSize)()這一句話將PayloadSize的uint數(shù)據(jù)轉(zhuǎn)為可供numpy處理的數(shù)據(jù),后面就可以用numpy將其轉(zhuǎn)化為numpy數(shù)組格式。
第三個(gè),線程的使用
官方例子是
hThreadHandle = threading.Thread(target=work_thread, args=(cam, None,None))hThreadHandle.start()此處需要改成
try:hThreadHandle = threading.Thread(target=work_thread2, args=(cam, data_buf, nPayloadSize))hThreadHandle.start()在這里,有些代碼可能會(huì)在data_buf前面加上byteref,如果這樣做的話,就會(huì)將數(shù)據(jù)轉(zhuǎn)為浮點(diǎn)型,而opencv需要的是整型,會(huì)報(bào)錯(cuò),所以這里就不需要轉(zhuǎn)化了。
本文僅做學(xué)術(shù)分享,如有侵權(quán),請(qǐng)聯(lián)系刪文。




