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."

Solved!
Go to solution

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 9
(196 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 9
(188 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 9
(154 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 9
(141 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 9
(130 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 9
(110 Views)
Solution
Accepted by topic author ddddd212

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
Message 7 of 9
(62 Views)

谢谢 rolfk,这真的很有帮助——彻底解开了很多疑惑。

你说得对,我和克劳德在错误的道路上耗费了太长时间。我们浪费了大量时间去追查“两个库实例”、SDK 版本不匹配、ABI 不兼容等等问题。结果发现真正的问题要根本得多:  LabVIEW IMAQ 引用句柄和图像指针根本不是一回事 。

如果其他人也遇到类似情况,以下是我们发现问题的简要总结:

  1. 最初我将 Image 参数设置为“适应类型,按值处理”——这会传递一些内部 LabVIEW 包装结构,而不是实际的 Image* 指针。

  2. 每次 NI Vision 调用(imaqThreshold、imaqGetImageType、imaqGetImageSize 等)均失败,错误代码为 -1074396120(“不是图像”)。

  3. 经过一系列测试(全局变量检查以确认单个 .so 实例、内存诊断、dlopen 比较),我发现将参数更改为“数值、无符号指针大小的整数、传递值”后,一切都正常了——imaqGetImageType 返回 0 (U8),imaqThreshold 返回 1,并且图像被正确二值化。

但根据你的解释,我现在明白了,这种“有效”的方法可能并未得到官方支持或任何文档记录。将 IMAQ 引用句柄作为指针大小的整数传递恰好有效,这很可能只是一个实现细节,未来版本可能会失效。

0 Kudos
Message 8 of 9
(19 Views)

Here is the exact solution to my problem:

change 

ddddd212_0-1775870283847.png

 

to 

ddddd212_1-1775870322663.png

 

There's a code I'm sending out as well for those who need it

0 Kudos
Message 9 of 9
(11 Views)