Blog.csdn.net/leo_888/art…
Blog.csdn.net/lyc_daniel/… Development of Sapera LT based on Qt
Application background: In the industrial detection of crystal surface defects, the pulse generated by the grating ruler feedback system of guide rail movement is used for external trigger Dalsa camera to collect images.
Problem solved: The image directly collected by Dalsa linear CCD is the image of the current row, and the buffer used to store the image in the supporting acquisition card is limited. When the platform moves continuously for a long distance, if the image in the buffer is not read in time, the new image will cover the image previously collected.
Read the inheritance diagram in the Dalsa camera development documentation as follows:
We are most concerned about the contents of the buffer, SapBuffer, and SapAcqToBuf, which transfers the collection contents to the buffer. If we are more careful, we can also see the Info of the callback function that transfers the collection contents to the buffer.
Check out some of the official development demos
Copy the code
// Transfer callback function is called each time a complete frame is transferred.
// The function below is a user defined callback function.
void XferCallback(SapXferCallbackInfo *pInfo)
{
// Display the last transferred frame
SapView *pView = (SapView *) pInfo->GetContext();
pView->Show();
}
// Example program
//
main()
{
// Allocate acquisition object
SapAcquisition *pAcq =
New SapAcquisition(SapLocation (" x64-cl_1 ", 0), "myCamera.ccf");
// Allocate buffer object, taking settings directly from the acquisition
SapBuffer *pBuffer = new SapBuffer(1, pAcq);
// Allocate view object, images will be displayed directly on the desktop
SapView *pView = new SapView(pBuffer, SapHwndDesktop);
// Allocate transfer object to link acquisition and buffer
SapTransfer *pTransfer = new SapTransfer(XferCallback, pView);
pTransfer->AddPair(SapXferPair(pAcq, pBuffer));
// Create resources for all objects
BOOL success = pAcq->Create();
success = pBuffer->Create();
success = pView->Create();
success = pTransfer->Create();
// Start a continuous transfer (live grab)
success = pTransfer->Grab();
printf("Press any key to stop grab\n");
getch();
// Stop the transfer and wait (timeout = 5 seconds)
success = pTransfer->Freeze();
success = pTransfer->Wait(5000);
printf("Press any key to terminate\n");
getch();
// Release resources for all objects
success = pTransfer->Destroy();
success = pView->Destroy();
success = pBuffer->Destroy();
success = pAcq->Destroy();
// Free all objects
delete pTransfer;
delete pView;
delete pBuffer;
delete pAcq;
return 0;
}
It is not difficult to find:
Firstly, Acquisition needs to be established, which includes device information, image information to be collected and Settings (such as image width and height), which can be quickly obtained by reading configuration files in the GUI dialog box of the official SDK.
Secondly, we set up the buffer we want. Since our basic configuration information is already acquired through Acquisition, we have selected one of the many overloaded functions of SapBuffer
Copy the code
SapBuffer(
int count,
SapXferNode* pSrcNode,
SapBuffer::Type type = SapBuffer::TypeScatterGather,
SapLocation loc = SapLocation::ServerSystem
);
Where count is the number of buffers, which obviously have the same size and data format as the image in the configuration file. SapXferNode is the parent class of SapAcquisition. The pointer to SapAcquisition is passed directly, followed by the default parameters.
Next comes the SapView class for displaying the image, binding the buffer to the window handle of the display control to display the image on the specified control. This has little to do with the acquisition process, but visualize this, you understand.
The next important step is to establish the transfer step from image data acquisition to buffer. Because it takes a long time to obtain the complete image (ideally the image height is set at 80000, but the buffer size is limited, set at 30000), there is enough time to read the buffer data. There is no collection rate higher than the transfer rate and garbage cache is not needed, so the following function is used
Copy the code
SapTransfer(
SapXferCallback pCallback = NULL,
void* pContext = NULL,
SapLocation loc = SapLocation::ServerUnknown
);
The first is the callback function, and the second is the context information for the callback function, which is essentially the argument to be passed to the callback function.
There is a paragraph in the SDK to explain how I use this linear CCD image and Acquisition card: I always mistakenly thought that an Acquisition was to acquire the current frame of image (i.e., the image acquired by a single exposure, or a row of images), but in fact it has been splice into the whole image (setting the image width and height in the information).
By default, regular and trash buffer callback functions are called at each end of frame event, that is, when a complete image has been transferred.
The SDK specifically states that
If you use this class , you must use the AddPair method to add transfer pairs of source and destination nodes. You must do this before calling the Create method.
So addPair was written next to bind the cache area to the collection area.
The next step is for each of them to Create the Create, using the default creation method. Other custom methods are not necessary and will not be used.
An initialization code was written in imitation
Copy the code
// TODO: Add additional initialization code here
CAcqConfigDlg dlg(this, NULL);
if (dlg.DoModal() == IDOK)
{
// Define on-line objects
m_pAcq = new SapAcquisition(dlg.GetAcquisition());
m_pBuffer = new SapBuffer(2, m_pAcq);
m_pView = new SapView(m_pBuffer, GetDlgItem(IDC_STATIC_VIEW)->GetSafeHwnd());
m_pTransfer = new SapTransfer(XferCallback, this);
m_pTransfer->AddPair(SapXferPair(m_pAcq, m_pBuffer));
}
else
{
// Define off-line objects
m_pBuffer = new SapBuffer();
}
m_pAcq->Create();
m_pBuffer->Create();
m_pTransfer->Create();
m_pView->Create();
The official GUI configuration dialog CAcqConfigDlg is used here, and two buffers are enabled. This is because in the process of continuous acquisition, if it takes time to read images, there is only one buffer, the first half of which will be covered by the new acquisition transfer, but when two buffers are enabled, The new image data can be transferred to another buffer, and this problem can be avoided by cyclic stagger.
The key is how the callback function is written. The previous code area is mainly used to display newly acquired images.
I’ll rewrite the code to save the image in time
Copy the code
void CDalsaCameraDlg::XferCallback( SapXferCallbackInfo *pInfo )
{
CDalsaCameraDlg* pDlg = (CDalsaCameraDlg*)(pInfo->GetContext());
int pitch = pDlg->m_pBuffer->GetPitch();
// Get the buffer data address
BYTE pData;
void* pDataAddr = &pData;
bool success = pDlg->m_pBuffer->GetAddress(staticCount, &pDataAddr);
int width = pDlg->m_pBuffer->GetWidth();
int height = pDlg->m_pBuffer->GetHeight();
Mat img = Mat::zeros(cv::Size(width, height), CV_8U);
memcpy(img.data, pDataAddr, width*height);
if (staticCount== 0)
{
imwrite("C:\\123.bmp", img);
staticCount = 1;
}
else (staticCount == 1)
{
imwrite("C:\\456.bmp", img);
staticCount = 0;
}
success = pDlg->m_pBuffer->ReleaseAddress(pDataAddr);
}
Because confirmation function can only be used static member function, therefore unable to invoke the static member function and non-static member variables, we need to read the contents of the buffer, therefore can only be the entire class pointer is passed to the callback function, and became a new class – this class (this is a bit like too b reality with lotus root to remake the feeling which zha)
This will manipulate the collected buffer class SapBuffer, using getAddress to get the first address of the image data, which is used here
BOOL GetAddress(int index, void** pData);
Index is the sequence number of the buffer, so that the control sequence number can read the data contents of the two buffers alternately. After each collection of a complete image, a buffer is read, and during this period of time, the new image is stored in the next buffer, there will be no coverage between the data, and the image can be read in time, saved in the hard disk.
The OpenCV image library is used to save images, but Windows Bitmap does not.
At the end of the program, remember to release the newly opened pointer variable
Copy the code
void CDalsaCameraDlg::OnDestroy()
{
CDialogEx::OnDestroy();
// TODO: Add message handler code here
// Release and free resources for SapBuffer object
if (nullptr ! = m_pView)
{
m_pView->Destroy();
delete m_pView;
}
if (nullptr ! = m_pTransfer)
{
m_pTransfer->Destroy();
delete m_pTransfer;
}
if (nullptr ! = m_pBuffer)
{
m_pBuffer->Destroy();
delete m_pBuffer;
}
if (nullptr ! = m_pAcq)
{
m_pAcq->Destroy();
delete m_pAcq;
}
}
It is better to release them in reverse order, for example, SapTransfer relies on binding Acquisition and Buffer, and if Acquisition and Buffer are released and destroyed first, When I tried to remove contact with Acquisition and Buffer when destroying SapTransfer, I found that I could not find these two.
I hope to finish my project as soon as possible. The development of this Dalsa camera is over.