11-22-2024 04:35 AM
I recently faced the same issue that IMAQ was not available in LabVIEW Community.
I worked it around with Python and TCP. It works in my case.
https://buymeacoffee.com/filippo.persia/labview-community-vision-acquistion-python
Br
Filippo
11-22-2024 05:56 AM
@Filippo_Persia wrote:
I recently faced the same issue that IMAQ was not available in LabVIEW Community.
I worked it around with Python and TCP. It works in my case.
https://buymeacoffee.com/filippo.persia/labview-community-vision-acquistion-python
Br
Filippo
You have huge overhead with this solution, to be honest.
First — when using Python
Second — when using TCP
Third — when using BMP format
Streaming over network with help of python makes sense in some architectures, of course, but why not using OpenCV directly in LabVIEW via simple wrapper, it is just fifty lines of code for trivial acquisition?
11-22-2024 06:03 AM
I agree there is overhead.
But is working fine and is simple. I cobbled it into working condition within 1h or so.
In my setup, an old i7 with 4GB of ram nothing fancy, I can run around 25ms refresh rate. It's ok as a fotofinish support.
Clearly is not for industrial grade vision application, but neither is labview community.
br
Filippo
11-22-2024 07:05 AM
@Filippo_Persia wrote:
I agree there is overhead.
But is working fine and is simple.
Well, if I will do this, then probably something like that (using OpenCV 4.10, in assumption that we have single cam):
#include <opencv2/opencv.hpp>
#include "framework.h"
#include "LVopenCVcamDLL.h"
using namespace cv;
using namespace std;
static VideoCapture cap;
static Mat frame;
Function to Open Camera:
LVOPENCVCAMDLL_API int fnOpenCam(int CamIdx)
{
VideoCapture video(CamIdx);
cap = video;
return cap.isOpened()? 0:-1;
}
Then get size of the image:
LVOPENCVCAMDLL_API int fnGetSize(int *Width, int *Height)
{
*Width = (int)cap.get(CAP_PROP_FRAME_WIDTH);
*Height = (int)cap.get(CAP_PROP_FRAME_HEIGHT);
return 0;
}
This is how to get frame:
LVOPENCVCAMDLL_API int fnGetFrame(uint8_t* dst, int LVWidth, int LVHeight)
{
if(!cap.read(frame) || !dst) return -1;
// Copy, line by line, the frame into the LabVIEW Arrray (must be allocated outside)
for (int y = 0; y < LVHeight; ++y) {
memcpy(dst + y * LVWidth * 3, (uint8_t*)frame.data + y * LVWidth * 3, LVWidth * sizeof(uint8_t) * 3);
}
return 0;
}
and close, of course:
LVOPENCVCAMDLL_API void fnCloseCam()
{
cap.release();
}
This is how it can be used all together with simple FPS counter:
That is. Also "not for industrial grade" with minimal error checking, but works for me.
Using DLL is dangerous in term of how easy to crash everything, so some code needs to be added for robustness. For multicam acquisition thread safety needs to be added as well.
But it is just a "lunch break" example, very quick and very very dirty.
I will drop sources on the GitHub (because size of the attachment is limited here), may be will be useful for someone...
11-24-2024 02:11 AM
In order to make this a little more useful for everyone, I have turned CLFNs into SubVIs (+ some other minor changes) during my Sunday morning coffee today. Now, a very basic acquisition example looks like this:
11-24-2024 02:55 AM
Nice. This is really neat.
I'll try it out as soon as I can.
Out of curiosity what is the fps you get?
Filippo
11-24-2024 03:38 AM - edited 11-24-2024 03:44 AM
@Filippo_Persia wrote:
Nice. This is really neat.
I'll try it out as soon as I can.
Out of curiosity what is the fps you get?
Filippo
If you mean FPS from a performance point of view, then I have the full rate delivered from the cam (30 FPS in my case) even on a relatively slow "kitchen" laptop, which has an i7 4th gen processor (10 years old, I guess).
By the way, the Get Frame was also slightly optimized:
now 2D array of RGB defined and passed to DLL as:
typedef struct {
int32_t dimSizes[2];
uint32_t pixel[1];
} ImageArr, ** ImageArrHdl;
directly resized and filled:
LVOPENCVCAMDLL_API void fnGetFrame(ImageArrHdl image, LVError* Error)
{
// .. some errors checks
MgErr err = NumericArrayResize(uL, 2, (UHandle*)&(image), width * height);
if (err) { SetError(Error, true, err, "NumericArrayResize() Err"); return; };
(*(image))->dimSizes[0] = height; // rows;
(*(image))->dimSizes[1] = width; // cols;
uint32_t* pixel_out = (*(image))->pixel;
uint8_t* pixel_in = frame.data;
if (!pixel_in || !pixel_out) { SetError(Error, true, OCV_NULL_POINTER, "Null pointer"); return; };
// Copy, pixel by pixel, the frame into the LabVIEW Arrray
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
*pixel_out++ = (*pixel_in << 16) | (*(pixel_in + 1) << | (*(pixel_in + 2));
pixel_in += 3;
}
}
}
LabVIEW Error Cluster also passed as parameter:
typedef struct {
LVBoolean status;
int32_t code;
LStrHandle source;
} LVError;
and set like this:
MgErr SetError(LVError* Error, LVBoolean Status, int32_t Code, const char* Source)
{
Error->status = Status;
Error->code = Code;
MgErr err = NumericArrayResize(uB, 1, (UHandle*)&(Error->source), strlen(Source));
if (!err) {
MoveBlock(Source, LStrBuf(*(Error->source)), strlen(Source));
LStrLen(*(Error->source)) = (int32)strlen(Source);
}
return err;
}
So, everything is pretty optimal now, and the bottleneck is Picture Control and "Draw True-Color Pixmap.vi". This SubVI can be optimized as well, but I don't think this is really necessary, as long as almost any average modern PC nowadays should be able to grab and display real-time images from any average USB cam also with this "Draw True-Color Pixmap.vi" implementation.
11-24-2024 05:01 PM
Thanks for sharing. Do you think it's beneficial to return a LabVIEW error cluster? National Instruments prefers to return an error code with a subsequent VI to translate it into a LabVIEW error cluster, for example, in DAQmx.
On one hand, creating the error in LabVIEW makes it easier to add the call hierarchy. On the other hand, the DLL code is better suited to identify the specific error, reducing the need for additional management since it serves as the single source of truth. Also, I noticed you're in HH too! 😊
11-24-2024 11:34 PM
@Quiztus2 wrote:
Thanks for sharing. Do you think it's beneficial to return a LabVIEW error cluster? National Instruments prefers to return an error code...
Oh, this is just the "NI IMAQ Vision Style," where the DLL returns void, and error handling is done inside the call:
There are no principal advantages or disadvantages; it is just a matter of convenience. In C, it can be a bit easier with the help of the preprocessor and some macros. If I'm dealing with DLLs anyway, I usually prefer to have less LabVIEW code and more C — just because I can. In the past, I've used a call chain here, but currently, NI has introduced an annoying issue...