Как говорил Генрих VIII очередной жене, — «Я вас долго не задержу...»
Если вас, уважаемый читатель, угораздило приобрести видеокамеру, поддержка которой не обеспечена библиотекой OpenCV, а методы работы оной с изображениями ой как нужны, не следует расстраиваться.
Сперва изучим, что нам преподнесли, или что сами, несведующие, купили.
- Интерфейс GigE поддерживается SDK, лежащей в свободном доступе или поставленной совместно с товаром. Первый плюс!
- Документация более-менее осмыслена. Снова повезло!
- Примеры есть! Надо же… Плюс!
Итак, я стал «счастливчиком» по плюсикам этого списка, заполучив камеру Smartek Giganetix GC1921M
Методы SDK работают, но как-то так… Код частично закрыт. Уровень программиста стал понятен из фрагмента кода
...
if (m_selectedDevice->IsConnected()){
m_disconnectAct->setEnabled(true);
m_fwUpdateAct->setEnabled(true);
}
...
Если у вас возник вопрос, — «А что тут такого?», — я не смогу отправить вас на машине времени в советский вуз, где за это с вас снимут балл на экзамене. :)
Да, и ладно. Нам-то нужно, всего лишь, подключиться, принять поток и отключиться. Благо, примеры — на месте.
Варианты реализаций собственного устройства видеозахвата.
Спасибо разработчикам библиотеки OpenCV за код, распрпостраняемый под BSD лицензией.
Речь идёт о версии 2.4.2.
Изучим часть исходников в каталоге modules/highgui/src. Как видим, всё несложно. Можно, просто скопировав похожий модуль, например cap_pvapi.cpp, под собственное устройство cap_giganetix.cpp сделать надлежащие правки, внести изменения (новый метод и элементы перечислений enum) в код precomp.hpp и соответствующие файлы директив для cmake и запустить сборку.
Это — первый метод. Основной минус — всё меняется от версии к версии, и пересборки новых релизов неизбежны.
Существует и более стабильный, если так можно назвать, вариант — реализация собственного CvCaptute для нового устройства, совместимого со структурой из OpenCV. Плюс в том, что разработчик помещает всю эту байду все свои зазработки в отдельную библиотеку, которая по требованиям зависимостей подгрузит и SDK на новое устройство, и OpenCV.
Минус, как всегда, в наличии «кота в мешке» — реализации на стороне разработчиков OpenCV скрытой структуры CvCapture.
Рассмотрим последний вариант.
Внешняя библиотека устройства видеозахвата типа CvCapture.
Вначале было слово обеспечим совместимость с макросами, значениями перечислений исходных и заголовочных файлов OpenCV.
macros.hpp
#ifndef MACROS_HPP
#define MACROS_HPP
#define QTGIG_HEARTBEAT_TIME (12000.0)
#define QTGIG_MAX_WAIT_TIME (2.0)
#define QTGIG_IMG_WAIT_TIME (3.0)
#define CV_CAP_GIGANETIX 1300
// GigE additional for highgui_c.h
//enum {
#define CV_CAP_PROP_GIGA_FRAME_SENS_WIDTH 40
#define CV_CAP_PROP_GIGA_FRAME_SENS_HEIGH 41
#define CV_CAP_PROP_GIGA_FRAME_WIDTH_MAX 42
#define CV_CAP_PROP_GIGA_FRAME_HEIGH_MAX 43
#define CV_CAP_PROP_GIGA_FRAME_OFFSET_X 44
#define CV_CAP_PROP_GIGA_FRAME_OFFSET_Y 45
//};
//precomp.hpp double
#define __BEGIN__ __CV_BEGIN__
#define __END__ __CV_END__
#define EXIT __CV_EXIT__
#endif // MACROS_HPP
Участок
//precomp.hpp double
#define __BEGIN__ __CV_BEGIN__
#define __END__ __CV_END__
#define EXIT __CV_EXIT__
приведёт код к правилам разработки модулей OpenCV. В чужой монастырь со своим уставом не лезь!
А так, мы просто добавили некоторые свойства для нашего устройства.
Создадим методы-оболочки для функций SDK нашей камеры. Это позволит минимизировать изменения в нашем коде при возможной правке разработчиками SDK собственного. Думаю, это — хороший стиль.
gige_wrapper.h
#ifndef GIGE_WRAPPER_H
#define GIGE_WRAPPER_H
/**
module GIGA_WRAPPER
brief Smartek Giganetix Cameras wrapper
*/
#include <GigEVisionSDK.h>
namespace gigew {
/*----------------------------------------------------------------------------*/
/**
internal
fn bool gigew::wrprInitGigEVisionAPI();
brief Wrapper to GigEVisionAPI function gige::InitGigEVisionAPI ()
return true - success
See a gigew::wrprExitGigEVisionAPI
*/
bool
wrprInitGigEVisionAPI();
/*----------------------------------------------------------------------------*/
/**
internal
fn void gigew::wrprExitGigEVisionAPI()
brief Wrapper to GigEVisionAPI function gige::ExitGigEVisionAPI ()
return true -- success
See a gigew::wrprInitGigEVisionAPI
*/
bool
wrprExitGigEVisionAPI();
// и так далее...
} //namespace gigew
#endif // GIGE_WRAPPER_H
И, собственно, само наше устройство (cap_giganetix.h)
#ifndef CAP_GIGANENIX_H
#define CAP_GIGANENIX_H
#include <opencv2/highgui/highgui_c.h>
#include "GigEVisionSDK.h"
#include "../../common/macros.hpp"
#include <QObject>
#ifdef HAVE_GIGE_API
#if !defined WIN32 && !defined _WIN32 && !defined _LINUX
#define _LINUX
#endif
#if defined(_x64) || defined (__x86_64) || defined (_M_X64)
#define _x64 1
#elif defined(_x86) || defined(__i386) || defined (_M_IX86)
#define _x86 1
#endif
/*----------------------------------------------------------------------------*/
/**
internal
struct CvCapture
brief Copy OpenCV CvCapture internal release.
*/
struct CvCapture
{
virtual ~CvCapture() {}
virtual double getProperty(int) { return 0; }
virtual bool setProperty(int, double) { return 0; }
virtual bool grabFrame() { return true; }
virtual IplImage* retrieveFrame(int) { return 0; }
virtual int getCaptureDomain() { return CV_CAP_ANY; } // Return the type of the capture object: CV_CAP_VFW, etc...
};
/*----------------------------------------------------------------------------*/
/**
internal
class CvCaptureCAM_Giganetix
brief Capturing video from camera via Smartec Giganetix GigEVisualSDK
*/
class Q_DECL_EXPORT CvCaptureCAM_Giganetix : public CvCapture
{
public:
CvCaptureCAM_Giganetix();
virtual ~CvCaptureCAM_Giganetix();
virtual bool open( int index );
virtual void close();
virtual double getProperty(int);
virtual bool setProperty(int, double);
virtual bool grabFrame();
virtual IplImage* retrieveFrame(int);
virtual int getCaptureDomain()
{
return CV_CAP_GIGANETIX;
}
bool start ();
bool stop ();
protected:
void init ();
void grabImage ();
gige::IGigEVisionAPI m_api;
bool m_api_on;
gige::IDevice m_device;
bool m_active;
IplImage* m_raw_image;
UINT32 m_rawImagePixelType;
bool m_monocrome;
};
/*----------------------------------------------------------------------------*/
Q_DECL_EXPORT CvCapture* cvCreateCameraCapture_Giganetix( int index );
/*----------------------------------------------------------------------------*/
#endif
#endif // CQTGIGEVISIONCAPTURE_H
Обратите внимание на то, что объявление struct CvCapture
взято из исходных кодов OpenCV (каталог modules/highgui/src, файл precomp.hpp). Вот оно, узкое место библиотеки!
Пишем реализацию класса и метода cvCreateCameraCapture_Giganetix
.
Архив проекта заберите по ссылке
Приведу лишь код метода open
:
...
/*----------------------------------------------------------------------------*/
bool
CvCaptureCAM_Giganetix::open( int index )
{
bool b_ret = m_api_on;
CV_FUNCNAME("CvCaptureCAM_Giganetix::open");
__BEGIN__;
if(b_ret)
b_ret = m_api.IsValid ();
if(b_ret )
{
m_api->FindAllDevices (QTGIG_MAX_WAIT_TIME);
//TODO - serch device as DevicesList member
gige::DevicesList DevicesList = m_api->GetAllDevices ();
m_device = 0;
b_ret = false;
for (int i = 0; i < (int) DevicesList.size() && !b_ret; i++)
{
if((b_ret = i == index))
{
m_device = DevicesList[i];
b_ret = m_device->Connect ();
if(b_ret)
{
b_ret =
m_device->SetStringNodeValue("AcquisitionStatusSelector", "AcquisitionActive")
&&
m_device->SetStringNodeValue ("TriggerMode", "Off")
&&
m_device->SetStringNodeValue ("AcquisitionMode", "Continuous")
&&
m_device->SetIntegerNodeValue ("AcquisitionFrameCount", 20)
;
}
}
} // for
}
if(!b_ret)
{
CV_ERROR(CV_StsError, "Giganetix: Error cannot find cameran");
close ();
} else {
start ();
}
__END__;
return b_ret;
}
...
Показано, как использовать макросы согласно стилю программирования от OpenCV.
Файл проекта оформляем как динамически подключаемую библиотеку c обращением к SDK и OpenCV:
#-----------------------------------------------------------
TARGET = QtGigEVisionCapture
TEMPLATE = lib
#CONFIG += release
#-----------------------------------------------------------
DEFINES += QTGIGEVISION_LIBRARY
HAVE_GIGE_API
#-----------------------------------------------------------
SOURCES +=
../../common/QtGigEVision_global.cpp
cap_giganetix.cpp
gige_wrapper.cpp
HEADERS +=
../../common/QtGigEvision_global.h
../../common/macros.hpp
cap_giganetix.h
gige_wrapper.h
#-----------------------------------------------------------
unix:!symbian: target.path = /usr/lib
INSTALLS += target
#-----------------------------------------------------------
unix:!macx:!symbian: LIBS += -L/usr/local/lib/ -lGigEVisionSDK
INCLUDEPATH += /usr/local/include/GigEVisionSDK/gige_cpp
/usr/local/include/GigEVisionSDK/gige_c
DEPENDPATH += /usr/local/include/GigEVisionSDK/gige_cpp
/usr/local/include/GigEVisionSDK/gige_c
#-----------------------------------------------------------
unix: LIBS += -L/usr/lib/ -lopencv_core -lopencv_highgui
INCLUDEPATH += /usr/include/opencv2/core
/usr/include/opencv2/highgui
DEPENDPATH += /usr/include/opencv2/core
/usr/include/opencv2/highgui
Под привилегиями cуперпользователя в каталоге /usr/lib размещаем ссылки на новую библиотеку и… приступаем к тестированию.
Тестовый пример.
Просто до безобразия, как два…
Одним словом, как-то так:
#include <QtGui/QApplication>
#include "../../common/macros.hpp"
#include <stdio.h>
#include "cap_giganetix.h"
void print_properties (CvCaptureCAM_Giganetix* cap)
{
if(cap) {
printf("Device found.n");
printf("Sensor Width = %d.n", (int)cap->getProperty (CV_CAP_PROP_GIGA_FRAME_SENS_WIDTH));
printf("Sensor Height = %d.n", (int)cap->getProperty (CV_CAP_PROP_GIGA_FRAME_SENS_HEIGH));
printf("Offset X = %d.n", (int)cap->getProperty (CV_CAP_PROP_GIGA_FRAME_OFFSET_X));
printf("Offset Y = %d.n", (int)cap->getProperty (CV_CAP_PROP_GIGA_FRAME_OFFSET_Y));
printf("Width = %d.n", (int)cap->getProperty (CV_CAP_PROP_FRAME_WIDTH));
printf("Height = %d.n", (int)cap->getProperty (CV_CAP_PROP_FRAME_HEIGHT));
printf("Frame Count = %d.n", (int)cap->getProperty (CV_CAP_PROP_FRAME_COUNT));
printf("Gain = %d.n", (int)cap->getProperty (CV_CAP_PROP_GAIN));
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CvCapture* capture = cvCreateCameraCapture_Giganetix(0);
CvCaptureCAM_Giganetix* cap = (CvCaptureCAM_Giganetix*)capture;
int i_width, i_height, i_offX, i_offY;
if(cap) {
i_width = (int)capture->getProperty (CV_CAP_PROP_FRAME_WIDTH);
i_height = (int)capture->getProperty (CV_CAP_PROP_FRAME_HEIGHT);
i_offX = (int)capture->getProperty (CV_CAP_PROP_GIGA_FRAME_OFFSET_X);
i_offY = (int)capture->getProperty (CV_CAP_PROP_GIGA_FRAME_OFFSET_Y);
printf("-------------------------n");
print_properties (cap);
printf("-------------------------n");
...
INT64 i = 0;
cvNamedWindow("Frame",0);
while(1) {
if(i == 1000) {
printf("------ Reset to original -------n");
(void)capture->setProperty (CV_CAP_PROP_FRAME_WIDTH, i_width);
(void)cap->setProperty (CV_CAP_PROP_FRAME_HEIGHT, i_height);
(void)capture->setProperty (CV_CAP_PROP_GIGA_FRAME_OFFSET_X, i_offX);
(void)cap->setProperty (CV_CAP_PROP_GIGA_FRAME_OFFSET_Y, i_offY);
print_properties (cap);
}
i++;
IplImage* frame = cvQueryFrame (capture);
if(frame)
cvShowImage ("Frame",frame);
if((cvWaitKey (3) == 27)) break;
}
cvDestroyWindow("Frame");
cvReleaseCapture(&capture);
}
return 0;//a.exec();
}
Рензультат — что-то размывчатое (:)):
Спросите, зачем я использую два указателя capture
и cap
на ...? Не спросите? Очень хорошо!
С уважением.
Успехов!
Автор: coffeesmoke