LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

"Problems developing a vision algorithm shared library (.so) on the NI Linux Real-Time x64 platform."

I am trying to develop a vision algorithm on the IC-3173, so I plan to start with a simple binarization to test it out. I referred to the C code generated by Vision Assistant 2019 (32-bit) and followed NI's official tutorial to generate the .so file. Everything seemed to go 'smoothly,' but the image never gets processed (no binarization happens, and the image remains unchanged).

Official Tutorial Website:NI Linux Real-Time Cross Compiling: Using the NI Linux Real-Time Cross Compile Toolchain with Visual...

0 Kudos
Message 1 of 7
(157 Views)

This is the LabVIEW RT part. If anyone has experience developing on NI Linux Real-Time x64, I would like to discuss it with you. The problem I'm encountering right now is that the nivision loaded by dlopen and LabVIEW's nivision are always two separate instances, and the Image handles cannot be shared between them.

 

Snipaste_2026-04-09_13-39-04.png

 

0 Kudos
Message 2 of 7
(149 Views)

Is this for educational purposes about creating your own shared libraries doing some vision operations or is your problem about what you try to do in that external code? If it is the second, why not use the built in LabVIEW IMAQ Threshold function?

 

I haven't tried to use IMAQ Vision like you do, but that two library problem sounds bogus, unless you have for some reason multiple nivision.so files on your system and the one you try to load explicitly through a full path is a different one than what the Linux ldd loader resolves to and loads when LabVIEW requests just libnivision.so from it. (Well the Call Library Node references nivision.* but that is changed to libnivision.so when requesting it from dlopen() ). Have you tried to just request dlopen("libnivision.so", RTLD_NOW) ? If that fails when the according function is called from a LabVIEW program that has at least one other IMAQ Vision vi in one of its diagrams, you have a real problem.

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
0 Kudos
Message 3 of 7
(115 Views)

Hello! Thank you very much for your reply. Since I need to implement a vision algorithm that is not built into NI Vision, I first need to successfully get image binarization to work. Also, I tried dlopen("libnivision.so", RTLD_NOW) but it failed during testing; only using the full path worked.

0 Kudos
Message 4 of 7
(102 Views)

@ddddd212 wrote:

Hello! Thank you very much for your reply. Since I need to implement a vision algorithm that is not built into NI Vision, I first need to successfully get image binarization to work. Also, I tried dlopen("libnivision.so", RTLD_NOW) but it failed during testing; only using the full path worked.


Then something with your ldd configuration is seriously different to what I have any experience with. /usr/local/natinst/lib should be basically part of the ldconfig search path.

 

Another thing that feels really wrong is your claim about the image identifiers not matching between the two types of calling libnivision functions. This is a very weird claim, since the IMAQ refnums are not managed by nivision, but by the actual LabVIEW runtime engine. libnivision.so simply calls into the runtime engine to create and manage these refnums. So even if your claim was true that libnivision.so is loaded twice, which still sounds very weird to me, it should still link back to the same runtime engine that started up the rtexe process.

 

You can try something else. Create your own shared library with two functions to get and set a global variable value. Install it in above path, open it once from a Call Library Node and a second time from another so that you call from the LabVIEW program. If they don't share the global data something is really off.

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 5 of 7
(91 Views)

 


@rolfk  :



Thank you again for your patience and reply! You made a valid point that the Image reference is not managed by libnivision, but by the LabVIEW runtime engine. So I rewrote the code using Claude and used imaqGetLastError() to retrieve error code -1074396120, which is explained in nivision.err as 'Not an image.' Claude then analyzed it and told me that the Image structure definition in the SDK 18.0 header files does not match the libnivision version 19.0 on the target machine. I don't really agree with Claude's explanation — I downloaded the GNU C & C++ Compilers for x64 Linux (Windows host) 2018-2019 version, which should be correct, and my IC-3173 software is also the 2019 version. This issue is really frustrating.

ddddd212_0-1775788652836.png
XZ_GetVersion returns 7001, XZ_GetImageType returns -2, XZ_IMAQ_Threshold returns 1074386120.

 

0 Kudos
Message 6 of 7
(71 Views)

I looked into this a bit more and Claude sort of got it almost right but completely went into the woods trying to explain the actual problem.

 

The Image datatype is a so called incomplete datatype. It's defined as a C struct without any known content. In C this is valid as long as you only pass around a pointer to this datatype. The compiler doesn't care about the content of a pointer and hence has no problem handling a pointer that points at some memory area that it simply knows nothing about. The LabVIEW Import Library Wizard doesn't like that, LabVIEW doesn't really know pointers in the context of a diagram. They are the ultimate thing in unsafe content in terms of a programming language and LabVIEW goes to great lengths to only allow elements that are safe to handle for it. The import Library Wizard simply could use a pointer sized integer for this but the developers of that wizard chose to not go into that potential cesspool of trouble.

 

The LabVIEW IMAQ refnum is NOT a pointer, it is a LabVIEW reference to an internal object in a registry like database. Accordingly what you pass in from the LabVIEW diagram into your C function can not be passed to any of the imaq.. functions. The LabVIEW IMAQ refnum is something completely different than the Image* data pointer that these functions expect. They ultimately resolve to the same actual memory object but the relationship between the LabVIEW refnum and the Image* pointer are not officially documented to my knowledge. The according LV_ APIs that the IMAQ VIs calls into all call a function LV_LVDTToGRImage from nivissvc.dll/so to convert the LabVIEW refnum to a GRImage datatype. But nivissvc is entirely undocumented. It's possible that the GRImage is the library internal datatype for the anonymous Image datatype from the nivision API. But there is no documentation about it and even if it was equivalent, there is no guarantee that it will always stay that way and if there are special considerations to be observed when treating them as equivalent.

 

Basically Claude correctly identified the datatype mismatch but attributed it to different SDK versions instead of different APIs. The fact that Image is an anonymous datatype exactly avoids any possibility of a version mismatch, since there are no callers outside of NI internal APIs who make any assumptions about the contents of that Image datatype. Yes you better make sure to not mix and match different DLL components from different NI Vision versions, but as long as you go with official NI Vision installers the NI installer takes care of that.

 

So in conclusion, you were all along barking up the wrong tree. There is no officially documented way you could pass LabVIEW IMAQ refnums to your DLL and then operate with imaq... functions from nivision.dll/so on them. The LabVIEW IMAQ refnum is NOT an Image* pointer and there is no officially documented and sanctioned method for you to convert one to the other. What you can do is to call the IMAQ GetImagePixelPtr VI in your diagram and pass that information to your shared library. But this is going to be somewhat painful. You get a pointer to the memory area that points to the first visible pixel of the image. But the entire memory area is larger because of the image border (typically 3 pixels on each side) and possible padding for memory alignment. When traversing the pixels in that memory area you have to account for the image data type, that makes a pixel anything from 1 byte up to 8 byte, and for the entire line width which includes the borders and padding. The line width in bytes is the returned LineWidth(Pixels) * PixelSize(Bytes). If your function is supposed to work on any of the possible IMAQ datatypes, this is the only really flexible solution, but your C code is going to be very involved!

 

If you only want to operate on a specific image datatype, another option might be to retrieve the actual pixel values as a 2D array of pixels with the IMAQ ImageToArray VI and operate on the according array in your C code.

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
0 Kudos
Message 7 of 7
(23 Views)