目录
原理
色彩空间
Bayer 与 deBayer
图像数据处理
代码实现
前期准备
完整代码
图片信息的表示不止只有我们熟知的RGB,还有HLS、HSV、YUV、bayer,由于opencv等视觉处理库的广泛运用,RGB空间色彩模型运用最为广泛(实际上是BGR)。
摄像头传感器的感光原理是通过一个个的感光点对光进行采样和量化。但是每一个感光点只能感光RGB中的一种颜色。所以通常所说的50万像素或5000万像素等,指的是有50万或5000万个感光点。每一个感光点只能感光一种颜色,但一个真正图像需要每一个像素点都有RGB三种颜色分量。
在传感器模组的内部有ISP模块,主要用来对前端图像传感器输出信号进行处理,将传感器采集到的数据进行插值和特效处理。例如一个感光点感应的颜色是R,则ISP模块就会根据这个感光点邻域的G、B感光点的数值来得出此点的G、B值,那么,当前像素点的RGB三个通道的值就被还原了。所以也可以这么说,现实世界中相机拍摄不存在不失真的图片。
Bayer格式源自拜尔阵列,拜尔阵列式CCD或CMOS传感器拍摄彩色图像的主要技术之一。拜耳阵列是由伊士曼·柯达公司科学家Bryce Bayer发明的,被广泛运用数字图像。
对于彩色图像的采集,最简单的方法就是对于每一个像素点用红绿蓝三种滤镜进行三个值的信号采集。但是传感器中单独的一个感光点只能接收感光信号,无法区分出感光信号种类。而用bayer阵列解决了这个问题,bayer阵列每个感光点只用一种颜色的滤镜。由于人眼对绿色较为敏感,所以整个传感器的感光点中采集绿色通道的感光点是其他两个通道的两倍,bayer的色彩滤波阵列G占1/2,R和B各占1/4。如下图所示
从上述1中可以看出,工业相机获得的初始图像信息并非为RGB图像信息,而是RAW格式的图像数据。其中RAW格式的原始图像数据Size为Wight*Height,经过色彩还原后的size为Wight*Height*3。
如下流程图所示,相机从CCD/CMOS获取模拟信号,经数模转换后,得到了原始的数字信号,由于需要考虑到原始数据的大小以及传输带宽,需要进行相机内部的预处理,对原始信号进行压缩,得到了RAW格式的原始图像数据。RAW为单通道size为Wight*Height的Bayer数据,需要通过debayer(解拜尔算法)转换成具有三通道的RGB图像。
实现python调用摄像头实时显示的效果如下
下载HK的MVS,安装好后在其安装文件里有python 的开发案例,在HK的开发案例中进行修改,实现基于python和opencv的gige接口工业相机的图片采集。
将案例中的MVImport单独拷出到新的文件夹,新建一个主程序文件GigEPy.py,其余的均为MVImport拷出的import程序,还有主要把MvCameraControl.dll拷进去,一般在C:\Program Files (x86)\Common Files\MVS\Runtime\Win64_x64这个路径里
接下来对GigEPy.py进行相机的调用和图片采集的代码设计
下图为调用Gige接口的流程图
其中最主要的部分是 ret = cam.MV_CC_GetImageBuffer(stOutFrame, 1000) 取出stOutFrame图像帧后要通过 cdll.msvcrt.memcpy(byref(data_buf), stOutFrame.pBufAddr, stOutFrame.stFrameInfo.nFrameLen) 取出RAW数据 然后通过Bayer 转换成BGR 得到图片
import sys
import threading
import msvcrt
import cv2
import numpy as np
import time
from ctypes import *
sys.path.append("../MvImport")
from MvCameraControl_class import *
g_bExit = False
def main():
deviceList = MV_CC_DEVICE_INFO_LIST()
tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE
# ch:枚举设备 | en:Enum device
ret = MvCamera.MV_CC_EnumDevices(tlayerType, deviceList)
if ret != 0:
print("enum devices fail! ret[0x%x]" % ret)
sys.exit()
if deviceList.nDeviceNum == 0:
print("find no device!")
sys.exit()
print("Find %d devices!" % deviceList.nDeviceNum)
for i in range(0, deviceList.nDeviceNum):
mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
print("\ngige device: [%d]" % i)
strModeName = ""
for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName:
strModeName = strModeName + chr(per)
print("device model name: %s" % strModeName)
nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24)
nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16)
nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8)
nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff)
print("current ip: %d.%d.%d.%d\n" % (nip1, nip2, nip3, nip4))
elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE:
print("\nu3v device: [%d]" % i)
strModeName = ""
for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName:
if per == 0:
break
strModeName = strModeName + chr(per)
print("device model name: %s" % strModeName)
strSerialNumber = ""
for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber:
if per == 0:
break
strSerialNumber = strSerialNumber + chr(per)
print("user serial number: %s" % strSerialNumber)
# nConnectionNum = input("please input the number of the device to connect:")
nConnectionNum = 0
if int(nConnectionNum) >= deviceList.nDeviceNum:
print("intput error!")
sys.exit()
# ch:创建相机实例 | en:Creat Camera Object
cam = MvCamera()
# ch:选择设备并创建句柄 | en:Select device and create handle
stDeviceList = cast(deviceList.pDeviceInfo[int(nConnectionNum)], POINTER(MV_CC_DEVICE_INFO)).contents
ret = cam.MV_CC_CreateHandle(stDeviceList)
if ret != 0:
print("create handle fail! ret[0x%x]" % ret)
sys.exit()
# ch:打开设备 | en:Open device
ret = cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
if ret != 0:
print("open device fail! ret[0x%x]" % ret)
sys.exit()
# ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
if stDeviceList.nTLayerType == MV_GIGE_DEVICE:
nPacketSize = cam.MV_CC_GetOptimalPacketSize()
if int(nPacketSize) > 0:
ret = cam.MV_CC_SetIntValue("GevSCPSPacketSize", nPacketSize)
if ret != 0:
print("Warning: Set Packet Size fail! ret[0x%x]" % ret)
else:
print("Warning: Get Packet Size fail! ret[0x%x]" % nPacketSize)
stBool = c_bool(False)
ret = cam.MV_CC_GetBoolValue("AcquisitionFrameRateEnable", stBool)
if ret != 0:
print("get AcquisitionFrameRateEnable fail! ret[0x%x]" % ret)
sys.exit()
# ch:设置触发模式为off | en:Set trigger mode as off
ret = cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
if ret != 0:
print("set trigger mode fail! ret[0x%x]" % ret)
sys.exit()
# ch:开始取流 | en:Start grab image
ret = cam.MV_CC_StartGrabbing()
if ret != 0:
print("start grabbing fail! ret[0x%x]" % ret)
sys.exit()
##############################这里获得照片
# ## 采集单张
# src = GetImage(cam)
cv2.namedWindow('1', cv2.WINDOW_NORMAL)
# cv2.imshow("1",src)
# # cv2.imwrite("1.jpg", src)
##实时显示
keyValue = 0
while keyValue != ord('q'):
src = GetImage(cam)
cv2.imshow("1", src)
# cv2.waitKey(10) # time.sleep 延时没有用
keyValue = cv2.waitKey(10)
cv2.destroyAllWindows()
##############################
# try:
# hThreadHandle = threading.Thread(target=work_thread, args=(cam, None, None))
# hThreadHandle.start()
# except:
# print ("error: unable to start thread")
# g_bExit = True
# hThreadHandle.join()
# ch:停止取流 | en:Stop grab image
ret = cam.MV_CC_StopGrabbing()
if ret != 0:
print("stop grabbing fail! ret[0x%x]" % ret)
sys.exit()
# ch:关闭设备 | Close device
ret = cam.MV_CC_CloseDevice()
if ret != 0:
print("close deivce fail! ret[0x%x]" % ret)
sys.exit()
# ch:销毁句柄 | Destroy handle
ret = cam.MV_CC_DestroyHandle()
if ret != 0:
print("destroy handle fail! ret[0x%x]" % ret)
sys.exit()
keyValue = cv2.waitKey()
cv2.destroyAllWindows()
def GetImage(cam):
sec = 0
data_buf = None
stOutFrame = MV_FRAME_OUT()
memset(byref(stOutFrame), 0, sizeof(stOutFrame))
ret = cam.MV_CC_GetImageBuffer(stOutFrame, 1000)
if None != stOutFrame.pBufAddr and 0 == ret:
if data_buf == None:
data_buf = (c_ubyte * stOutFrame.stFrameInfo.nFrameLen)()
# print("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (
# stOutFrame.stFrameInfo.nWidth, stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nFrameNum))
cdll.msvcrt.memcpy(byref(data_buf), stOutFrame.pBufAddr, stOutFrame.stFrameInfo.nFrameLen)
temp = np.asarray(data_buf)
temp = temp.reshape((1944, 2592)) #注意 这里要改成使用相机的宽和高
temp = cv2.cvtColor(temp, cv2.COLOR_BayerBG2BGR)
nRet = cam.MV_CC_FreeImageBuffer(stOutFrame)
return temp
else:
print("no data[0x%x]" % ret)
if __name__ == "__main__":
main()