需要用摄像头,而实验室只有一个海康威视的MV-CA013-20GC(另一个大恒的更难搞),拿工业相机当普通相机用是不是有点暴殄天物了。
1. 相机接线
工业相机一般都需要独立供电,12v接上相机的正负极,另外网口接上电脑或交换机Lan口,建议使用千兆网口,不然帧率跟不上(也可以使用网口转usb3.0).
2.客户端使用
去官网下载下面两个软件安装,一个是客户端,另一个是开发运行环境。
打开软件如图所示
软件会自动搜索可连接的设备,查找到设备后修改相机的ip,连接上相机,然后就可以调节参数了。这里如果画面很黑,首先调小相机的光圈,增大进光量,开启自动曝光与自动增益、伽马校正。如果使用的百兆网口会出现帧率低的情况,这里要么就更换千兆网口,要么将图像类型设置为mono8.
3.sdk的二次开发
进入到安装目录下有一个Development文件夹,里面有头文件和库文件以及各种语言的开发说明和demo。
这里以C++为例(VS2019)
大致流程如下,具体实现请参考文档和demo
首先新建项目,将开发需要的头文件和库文件拷出来放到项目文件中,注意库文件有32和64之分。在属性里面配置头文件和库文件。如图所示
示例代码,先捕获摄像头图像,再用opencv显示
#include "MvCameraControl.h"
#include "opencv2/opencv.hpp"
#include <stdio.h>
#include <conio.h>
#include <iostream>
#include <string>
using namespace std;
bool printDeviceInfo(MV_CC_DEVICE_INFO* hk_device);
bool hk2cv(MV_FRAME_OUT_INFO_EX* hk_imginfo, unsigned char* data, cv::Mat &src_img);
int main(int argv, char** argc) {
int ret = MV_OK;
void* handle = NULL;
MV_CC_DEVICE_INFO_LIST hk_devices;
memset(&hk_devices, 0, sizeof(MV_CC_DEVICE_INFO_LIST));
ret = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &hk_devices);
if (ret != MV_OK) {
cout << "enum devices faild!" << endl;
return -1;
}
if (hk_devices.nDeviceNum > 0) {
MV_CC_DEVICE_INFO* hk_camera = hk_devices.pDeviceInfo[0];
if (printDeviceInfo(hk_camera) == false) {
return -1;
}
}
else {
cout << "no device found" << endl;
return -1;
}
ret = MV_CC_CreateHandle(&handle, hk_devices.pDeviceInfo[0]);
if (ret != MV_OK) {
return -1;
}
ret = MV_CC_OpenDevice(handle);
if (ret != MV_OK) {
return -1;
}
ret = MV_CC_SetEnumValue(handle, "TriggerMode", 0);
if (ret != MV_OK) {
return -1;
}
MVCC_INTVALUE hk_param;
memset(&hk_param, 0, sizeof(MVCC_INTVALUE));
ret = MV_CC_GetIntValue(handle, "PayloadSize", &hk_param);
if (ret != MV_OK) {
return -1;
}
unsigned int payload_size = hk_param.nCurValue;
// load config
ret = MV_CC_FeatureLoad(handle, "FeatureFile.ini");
if (ret != MV_OK){
cout << "loading config file faild" << endl;
return -1;
}
// save config
/* ret = MV_CC_FeatureSave(handle, "FeatureFile.ini");
if (ret != MV_OK) {
return -1;
}*/
// start grabbing images
ret = MV_CC_StartGrabbing(handle);
if (ret != MV_OK) {
cout << "grab image failed!" << endl;
return -1;
}
MV_FRAME_OUT_INFO_EX hk_imginfo;
memset(&hk_imginfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));
unsigned char* data = (unsigned char*)malloc(sizeof(unsigned char) * (payload_size));
if (data == NULL) {
return -1;
}
cv::Mat src_img;
while (!(_kbhit() && _getch() == 0x1b)) {
ret = MV_CC_GetOneFrameTimeout(handle, data, payload_size, &hk_imginfo, 1000);
if (ret != MV_OK) {
free(data);
data = NULL;
return -1;
}
if (hk2cv(&hk_imginfo, data, src_img) == false) {
continue;
}
cv::imshow("test", src_img);
cv::waitKey(30);
}
// stop grap image
ret = MV_CC_StopGrabbing(handle);
if (ret != MV_OK) {
return -1;
}
// close device
ret = MV_CC_CloseDevice(handle);
if (ret != MV_OK) {
return -1;
}
ret = MV_CC_DestroyHandle(handle);
if (ret != MV_OK) {
return -1;
}
system("pause");
return 0;
}
bool printDeviceInfo(MV_CC_DEVICE_INFO* hk_device) {
if (NULL == hk_device)
{
printf("The Pointer of hk_device is NULL!\n");
return false;
}
if (hk_device->nTLayerType == MV_GIGE_DEVICE)
{
int nIp1 = ((hk_device->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24);
int nIp2 = ((hk_device->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16);
int nIp3 = ((hk_device->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8);
int nIp4 = (hk_device->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff);
// print current ip and user defined name
printf("CurrentIp: %d.%d.%d.%d\n", nIp1, nIp2, nIp3, nIp4);
printf("UserDefinedName: %s\n\n", hk_device->SpecialInfo.stGigEInfo.chUserDefinedName);
}
else if (hk_device->nTLayerType == MV_USB_DEVICE)
{
printf("UserDefinedName: %s\n", hk_device->SpecialInfo.stUsb3VInfo.chUserDefinedName);
printf("Serial Number: %s\n", hk_device->SpecialInfo.stUsb3VInfo.chSerialNumber);
printf("Device Number: %d\n\n", hk_device->SpecialInfo.stUsb3VInfo.nDeviceNumber);
}
else
{
printf("Not support.\n");
}
return true;
}
bool hk2cv(MV_FRAME_OUT_INFO_EX* hk_imginfo, unsigned char* data, cv::Mat& src_img) {
cv::Mat cv_img;
if (hk_imginfo->enPixelType == PixelType_Gvsp_Mono8){
cv_img = cv::Mat(hk_imginfo->nHeight, hk_imginfo->nWidth, CV_8UC1, data);
}else if (hk_imginfo->enPixelType == PixelType_Gvsp_RGB8_Packed){
for (unsigned int j = 0; j < hk_imginfo->nHeight; j++){
for (unsigned int i = 0; i < hk_imginfo->nWidth; i++){
unsigned char red = data[j * (hk_imginfo->nWidth * 3) + i * 3];
data[j * (hk_imginfo->nWidth * 3) + i * 3] = data[j * (hk_imginfo->nWidth * 3) + i * 3 + 2];
data[j * (hk_imginfo->nWidth * 3) + i * 3 + 2] = red;
}
}
cv_img = cv::Mat(hk_imginfo->nHeight, hk_imginfo->nWidth, CV_8UC3, data);
}else{
printf("unsupported pixel format\n");
return false;
}
if (cv_img.data == NULL){
return false;
}
cv_img.copyTo(src_img);
return true;
}
因为每次断电后都要重新设置,这里我取了一个巧,先用客户端配置好参数,然后保存参数,以后每次运行只要加载参数就行了。如果运行提示没有找到dll文件,那么就是你runtime运行环境没有安装,或者没有将安装路径添加到系统环境变量。
正如开头所说的,只是拿来当普通摄像头用,所以代码很简单,后面有机会再深入研究。