python调用海康相机进行Apriltag检测
第一章:win10下安装Apriltag库
1.安装Apriltag库
打开cmd输入
pip install pupil-apriltags
这个是win下的,ubuntu下安装指令为
pip install apriltag
2.测试是否安装成功
#import apriltag #for linux
import pupil_apriltags as apriltag # for windows
import cv2
import numpy as np
import sys
img =cv2.imread("tag.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#at_detector = apriltag.Detector(apriltag.DetectorOptions(families='tag36h11 tag25h9') )#for linux
at_detector = apriltag.Detector(families='tag36h11 tag25h9') #for windows
tags = at_detector.detect(gray)
print("%d apriltags have been detected."%len(tags))
for tag in tags:
cv2.circle(img, tuple(tag.corners[0].astype(int)), 4,(255,0,0), 2) # left-top
cv2.circle(img, tuple(tag.corners[1].astype(int)), 4,(255,0,0), 2) # right-top
cv2.circle(img, tuple(tag.corners[2].astype(int)), 4,(255,0,0), 2) # right-bottom
cv2.circle(img, tuple(tag.corners[3].astype(int)), 4,(255,0,0), 2) # left-bottom
cv2.imshow("tag",img)
cv2.waitKey()
能够检测出四个角点即可。
注:选择的family要和你的图像对应,否则检测不出。
第二章:python调用海康相机进行Apriltag检测
1.安装MVS
这个到海康官网下载即可
MVS
默认安装就行
2.复制文件
找到MVS安装目录下的MvImport文件夹,并拷贝出来。
也可以在屏幕左下角“”“开始”---->MVS------>python samples找到该文件夹。
3.进行检测
# -- coding: utf-8 --
import sys
import numpy as np
import cv2
import time
import pupil_apriltags as apriltag # for windows
sys.path.append(r".\MvImport")
from MvCameraControl_class import *
def Enum_device(tlayerType, deviceList):
"""
ch:枚举设备 | en:Enum device
nTLayerType [IN] 枚举传输层 ,pstDevList [OUT] 设备列表
"""
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)
# 输出设备ID
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))
# 输出USB接口的信息
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)
def enable_device(nConnectionNum):
"""
设备使能
:param nConnectionNum: 设备编号
:return: 相机, 图像缓存区, 图像数据大小
"""
# ch:创建相机实例 | en:Creat Camera Object
cam = MvCamera()
# ch:选择设备并创建句柄 | en:Select device and create handle
# cast(typ, val),这个函数是为了检查val变量是typ类型的,但是这个cast函数不做检查,直接返回val
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)
# 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:Get payload size
stParam = MVCC_INTVALUE()
memset(byref(stParam), 0, sizeof(MVCC_INTVALUE))
# MV_CC_GetIntValue,获取Integer属性值,handle [IN] 设备句柄
# strKey [IN] 属性键值,如获取宽度信息则为"Width"
# pIntValue [IN][OUT] 返回给调用者有关相机属性结构体指针
# 得到图片尺寸,这一句很关键
# payloadsize,为流通道上的每个图像传输的最大字节数,相机的PayloadSize的典型值是(宽x高x像素大小),此时图像没有附加任何额外信息
ret = cam.MV_CC_GetIntValue("PayloadSize", stParam)
if ret != 0:
print("get payload size fail! ret[0x%x]" % ret)
sys.exit()
nPayloadSize = stParam.nCurValue
# ch:开始取流 | en:Start grab image
ret = cam.MV_CC_StartGrabbing()
if ret != 0:
print("start grabbing fail! ret[0x%x]" % ret)
sys.exit()
# 返回获取图像缓存区。
data_buf = (c_ubyte * nPayloadSize)()
# date_buf前面的转化不用,不然报错,因为转了是浮点型
return cam, data_buf, nPayloadSize
def get_image(data_buf, nPayloadSize):
"""
获取图像
:param data_buf:
:param nPayloadSize:
:return: 图像
"""
# 输出帧的信息
stFrameInfo = MV_FRAME_OUT_INFO_EX()
# void *memset(void *s, int ch, size_t n);
# 函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s
# memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法
# byref(n)返回的相当于C的指针右值&n,本身没有被分配空间
# 此处相当于将帧信息全部清空了
memset(byref(stFrameInfo), 0, sizeof(stFrameInfo))
# 采用超时机制获取一帧图片,SDK内部等待直到有数据时返回,成功返回0
ret = cam.MV_CC_GetOneFrameTimeout(data_buf, nPayloadSize, 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)
image = np.asarray(data_buf) # 将c_ubyte_Array转化成ndarray得到(3686400,)
#stFrameInfo.nHeight=2048
#stFrameInfo.nWidth=2448
image = image.reshape((stFrameInfo.nHeight, stFrameInfo.nWidth, -1)) # 根据自己分辨率进行转化
return image
def close_device(cam, data_buf):
"""
关闭设备
:param cam:
:param data_buf:
"""
# ch:停止取流 | en:Stop grab image
ret = cam.MV_CC_StopGrabbing()
if ret != 0:
print("stop grabbing fail! ret[0x%x]" % ret)
del data_buf
sys.exit()
# ch:关闭设备 | Close device
ret = cam.MV_CC_CloseDevice()
if ret != 0:
print("close deivce fail! ret[0x%x]" % ret)
del data_buf
sys.exit()
# ch:销毁句柄 | Destroy handle
ret = cam.MV_CC_DestroyHandle()
if ret != 0:
print("destroy handle fail! ret[0x%x]" % ret)
del data_buf
sys.exit()
del data_buf
def detect_circles_demo(image):
dst = cv2.pyrMeanShiftFiltering(image, 10, 100) # 均值偏移滤波
cimage = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(cimage, cv2.HOUGH_GRADIENT, 1, 20, param1=50, param2=40, minRadius=0, maxRadius=0)
# 整数化,#把circles包含的圆心和半径的值变成整数
if circles!=None:
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
cv2.circle(image, (i[0], i[1]), i[2], (0, 255, 0), 2)
cv2.circle(image, (i[0], i[1]), 2, (0, 0, 255), 3)
cv2.imshow("circles", image)
def line_detection_demo(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
lines = cv2.HoughLines(edges, 1, np.pi/180, 200) # 函数将通过步长为1的半径和步长为π/180的角来搜索所有可能的直线
if not lines is None:
for line in lines:
rho, theta = line[0] # line[0]存储的是点到直线的极径和极角,其中极角是弧度表示的
a = np.cos(theta) # theta是弧度
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + 1000 * (-b)) # 直线起点横坐标
y1 = int(y0 + 1000 * (a)) # 直线起点纵坐标
x2 = int(x0 - 1000 * (-b)) # 直线终点横坐标
y2 = int(y0 - 1000 * (a)) # 直线终点纵坐标
cv2.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2)
#cv2.imshow("image_lines", image)
if __name__ == "__main__":
# 获得设备信息
deviceList = MV_CC_DEVICE_INFO_LIST()
tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE
# ch: 枚举设备 | en:Enum device
# nTLayerType[IN] 枚举传输层 ,pstDevList[OUT] 设备列表
Enum_device(tlayerType, deviceList)
# 获取相机和图像数据缓存区
cam, data_buf, nPayloadSize = enable_device(0) # 选择第一个设备
at_detector = apriltag.Detector(families='tag25h9')
while True:
t0 = time.time()
image = get_image(data_buf, nPayloadSize)
#image=cv2.cvtColor(image,CV_BayerGR2BGR);
image=cv2.cvtColor(image,cv2.COLOR_BAYER_GR2RGB)
print('Done Grabimg. (%.3fs)' % (time.time() - t0))
t1 = time.time()
'''
#image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 默认是BRG,要转化成RGB,颜色才正常
#img = cv2.Canny(image,100,200)
line_detection_demo(image)
#cv2.namedWindow("image", cv2.WINDOW_NORMAL)
#cv2.imshow("image", image)
if cv2.waitKey(1) & 0xFF == ord('q'):
cv2.destroyAllWindows()
break
'''
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
tags = at_detector.detect(gray)
for tag in tags:
cv2.circle(image, tuple(tag.corners[0].astype(int)), 4, (255, 0, 0), 2) # left-top
cv2.circle(image, tuple(tag.corners[1].astype(int)), 4, (255, 0, 0), 2) # right-top
cv2.circle(image, tuple(tag.corners[2].astype(int)), 4, (255, 0, 0), 2) # right-bottom
cv2.circle(image, tuple(tag.corners[3].astype(int)), 4, (255, 0, 0), 2) # left-bottom
# 显示检测结果
#cv2.namedWindow("image", cv2.WINDOW_NORMAL)
cv2.resizeWindow('capture', (512, 512))
cv2.imshow('capture', image)
if cv2.waitKey(1) & 0xFF == ord('q'):
cv2.destroyAllWindows()
break
print('Done Apirltag. (%.3fs)' % (time.time() - t1))
# 关闭设备
close_device(cam, data_buf)