Continuous update of OpenCV and MFC cameras

Writing an image processing program with an interface, choosing OpenCV + MFC is a good choice; In terms of reading camera data, there are many ways on the Internet. Among them, CAMERADS Shiqiyu method is the best.

Based on the information found, through the accumulation of actual projects, I summarized a set of camera compilation frameworks that combine OpenCV and MFC. It has the following characteristics:

1. Based on DirectShow, good compatibility and fast speed. So far, whether it is an industrial camera or a general camera, it has not been found to be incompatible;

2. Read the camera part through the thread to ensure the smooth operation of the interface;

3. After many refining of the structure, it is relatively stable, and there will be no abnormal faults; The code is simple, clean and reusable.

edeeecc9e72352f9674977449adc05e7

1. Code analysis

The structure is created by the dialog mode and the Camerads class and CVVimage class are added.


b8e7af24545834f38da4cb92f32b3c7e

Camerads website is written by Shiqiyu and basically completes DirectShow rendering and introduces the following features. It can get the total number of cameras, read the camera name, open the camera, and get the current frame data.

//Откройте камеру, Ncamid указывает, какую камеру открыть, значение может быть 0,1,2, ...
//BdisplayProperties указывает, автоматически появляется страница атрибута камеры
//Ширина и высота камеры, установленной NWIDTH и Nheight, если камера не поддерживает ширину и высоту набора, возвращает ложь
boolCCameraDS::OpenCamera(int nCamID,bool bDisplayProperties=true,int nWidth=320,int nHeight=240);
//Выключите камеру, деструктор автоматически вызовет эту функцию
voidCloseCamera();
//Количество возвращающихся камер
//Вы можете использовать int c = ccamerads :: cameracount (), не создавая экземпляр Ccamerads; получить результат.
staticintCameraCount();
//Вернуться к названию камеры в соответствии с номером камеры
//Ncamid: номер камеры
//Sname: массив для хранения названия камеры
//nbuffersize: sname size
//Вы можете использовать ccamerads :: Cameraaname (), не создавая экземпляр Ccamerads; получить результат.
staticintCCameraDS::CameraName(int nCamID,char* sName,int nBufferSize);
//Вернуться к ширине изображения
intGetWidth(){return m_nWidth;}
//Вернуться к высоте изображения
intGetHeight(){return m_nHeight;}
//Возьмите кадр, и возвращенный iplimage не может быть выпущен вручную!
//Сверху вниз (первый байт-верхний левый пиксель левого углу) режима RGB, то есть iplimage :: Origin = 0 (ipl_origin_tl)
IplImage*QueryFrame();
voidDisplayPinProperties(void);

The CVVimage class is provided by OpenCV itself. The main purpose of using it here is to say that the MAT object is being dragged into an MFC control

CvvImage cimg;
IplImage cpy = dst;
cimg.CopyOf(&cpy );//  Копировать картинки
cimg.DrawToHDC( hDC,&rect );//  Нарисуйте изображение в оборудованную область управления дисплеем

This is the main code in Gomfctemplate2dlg, which is divided into the following parts. I mostly outline things in this piece.

1. The camera shows a cycle, which is a separate stream

//Цикл отображения камеры
DWORD WINAPI CaptureThread(LPVOID lpParameter)
{
CGOMfcTemplate2Dlg* pDlg =(CGOMfcTemplate2Dlg*)lpParameter;
while(true)
{
IplImage* queryframe = pDlg->cameraDs.QueryFrame();
Mat matframe(queryframe);//Iplimage в преобразование мата
if(pDlg->b_closeCam)//Выходная петля
break;
if(pDlg->b_takeApic )
{
pDlg->b_takeApic =false;
pDlg->m_mainframe = matframe;
Sleep(500);
}
pDlg->showImage(matframe,IDC_CAM);
}
return0;
}

This thread function reads the main DLG thread as a parameter at creation time so that the thread can communicate between threads. It mainly performs two tasks, one is to read the current image and pass it to the main thread via the QueryFrame Camerads function; The other is to determine whether the control variables B_CLOSECAM and B_TALEAPIC are true and perform their related operations.

The current way to share variables, adopted by inter-slot communication, is that because the variable is written to the camera stream, it is a variable read in the main thread, which usually does not conflict. However, if there are many cameras or a very long time, you should still use Postmessage to communicate.

2. initDialog, initialize the control interface

m_nCamCount =CCameraDS::CameraCount();//Общее количество камер
//Получить количество камер
char camera_name[1024];
char istr[25];
for(int i=0; i < m_nCamCount; i++)
{
int retval =CCameraDS::CameraName(i, camera_name,sizeof(camera_name));
sprintf_s(istr," # %d", i);
strcat_s(camera_name,istr );
CString camstr = camera_name;
if(retval >0)
m_CBNCamList.AddString(camstr);
else
AfxMessageBox(_T("Не могу получить имя камеры"));
}
//Инициализировать управление дисплеем
CRect rect;
GetDlgItem(IDC_CAM)->GetClientRect(&rect);
m_mainframe =Mat::zeros(rect.Height(),rect.Width(),CV_8UC3);
GetDlgItem(IDC_PIC)->GetClientRect(&rect);
m_takepic =Mat::zeros(rect.Height(),rect.Width(),CV_8UC3);
return TRUE;//  Если не установите фокус на управление, верните True

Including the combo control padding to create MAT variables corresponding to the appropriate size for the two static controls to display.

3. Open the camera, based on the selected camera name, create a camera stream

voidCGOMfcTemplate2Dlg::OnBnClickedBtnOpencam()
{
if(m_nCamCount>=1)//Открыть ветку захвата видео
{
HANDLE hThread = NULL;
DWORD dwThreadID =0;
OnBnClickedBtnClosecam();//Сначала закрыть существующую камеру
bool bret = cameraDs.OpenCamera(m_iCamNum,false,640,480);//Попробуйте открыть камеру
if(bret)
{
b_closeCam =false;
hThread =CreateThread(NULL,0,CaptureThread,this,0,&dwThreadID);
}
}
else
{
AfxMessageBox(_T("Пожалуйста, подтвердите, что по крайней мере некоторые камеры подключены"));
}
}

Minor modification can be used for multiple cameras. This is not a problem at all and leads to practical items.

4. Turn off the camera

voidCGOMfcTemplate2Dlg::OnBnClickedBtnClosecam()
{
//Попробуйте закрыть камеру
b_closeCam =true;
Sleep(100);
cameraDs.CloseCamera();
}

Move the camera head thread control variables and call the Camerads CloseCamera function to close the camera;

5. Collect photos

voidCGOMfcTemplate2Dlg::OnBnClickedBtnTakepic()
{
b_takeApic =true;
Sleep(100);
if(m_mainframe.rows >0)
{
showImage(m_mainframe,IDC_PIC);
}
}

Wrap the control variables in the camera head thread and display the image in the control.

6. Image display function,To easily talk about the MAT object displayed in an MFC control, write an image implementation function

voidCGOMfcTemplate2Dlg::showImage(Mat& src, UINT ID)
{
if(src.empty())
return;
CRect rect;
Mat dst = src.clone();
GetDlgItem(ID)->GetClientRect(&rect );//  Получите местоположение размера управления
if(dst.channels()==1)
cvtColor(dst, dst, CV_GRAY2BGR);
CDC* pDC =GetDlgItem( ID )->GetDC();
HDC hDC = pDC ->GetSafeHdc();//  Получить HDC (ручка устройства) для работы рисования
CvvImage cimg;
IplImage cpy = dst;
cimg.CopyOf(&cpy );//  Копировать картинки
cimg.DrawToHDC( hDC,&rect );//  Нарисуйте изображение в оборудованную область управления дисплеем
ReleaseDC( pDC );
}

The main thing is to call the DrawToHDC CVVImage function and perform appropriate error handling.

2. Existing problems

Since DirectShow itself is a COM architecture, the learning curve is quite steep. There are still at least two problems with this architecture:

1. The camera is aware of the configuration problem and maintenance problem. Currently, the camera configuration is not implemented in the chassis,This is how the dialog box appears, which can set the camera parameter in the figure below. The latest configuration data must be saved.

db3872f022a0153e22ae50b54cfac248

2. Problems collecting video danters.

I am currently using the XVID camera data collection method, which may solve the problem. But I think DirectShow should be the camera data itself and save in avi format. This aspect also needs more research.

I will continue to summarize this information in the next step. At the same time, I hope to get expert guidance, thank you!

Code location https://github.com/jsxyhelu/gomfctemplate2

[P.S] October 7, 2016, after just learning DirectShow, I mostly refer to AMPCAP Camera (VS2012 + Win7 can run https://git.coding.net/jsxyhelu/amca_win7vs2012.git). The problem is, at the same time, I also tried to solve the task problem. Testing on USB cameras and industrial cameras is no problem.

[P.S] I’m checking the framework on a freshly installed machine and there won’t be any live header file, but the OS version might be different. Some operating systems still need header files. Link: https://pan.baidu.com/s/1qxngzoo Download for the full version. If the connection fails, please contact me in time. Thankful.

Leave a Comment