LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Creating a DLL to work on 2D Arrays

Solved!
Go to solution

Unfortunately DeAllocate still returns 108 for me.

@Greg
IMHO: The approach of using a Cluster doesn't bring you any advantage, since your 1D array also has it's length allocated just in front of it.

See 'Uint16Array1Base' in your header:

 

 

 

typedef struct {
	int32_t dimSize;
	uint16_t Numeric[1];
} Uint16Array1Base;
typedef Uint16Array1Base **Uint16Array1;
typedef struct {
	Uint16Array1 Array;
	int32_t NumRows;
	int32_t NumCols;
} U162DArrayCluster;

 

 


So the Cluster with a 1D array is likely worse than using a 2D array directly, because of the additionals in your VI. My statement earlier about the possibility on zero copy was probably wrong. I forgot that LV 2D Array is not defined like:

 

 

typedef struct {
	int32_t dimSizes[2];
	uint16_t elt*;
} Uint16ArrayBase;

 

 

So if copying is not a showstopper, use LV Allocate and DeAllocate, defined in your genereted headers. Maybe rolfk knows why DeAllocation returns 108?

Message 11 of 47
(321 Views)
Solution
Accepted by Gregory

From initial question I didn't catch at all — if this DLL will be called from LabVIEW or from C?

In case if this DLL intendent for third-party environment, then probably I will not mix LabVIEW "managed" memory and external and then pass native pointers from C to LabVIEW code and call MoveBlock to copy to LabVIEW's array for manipulation.

For example, function to compute average of the U16 image could be like this:

av_snippet.png

And we don't need to deallocate LabVIEW's Array, because LabVIEW will take care automatically.

If we have source and destination, then it looks like this, for example, increment:

inc_snippet.png

Then C code will be very simple and straight forward:

#include <ansi_c.h>
#include "SharedLib.h"

int main (int argc, char *argv[])
{
	uint16_t *src, *dst;
	int width = 240, height = 320;
	
	src=(uint16_t *)malloc(width * height * sizeof(uint16_t));
	dst = (uint16_t *)malloc(width * height * sizeof(uint16_t));
	
	int sum = 0;
	for (uint16_t row = 0; row < height; row++){
		for (uint16_t col = 0; col < width; col++){
			src[col + row * width ] = row + col;
			sum += row + col;
		}
	}
	float testAverage = (double)sum/(double)(width * height);
	int ret = IncImage((int64_t)src, (int64_t)dst, width, height); //call from LabVIEW's DLL	
	float srcAverage = ImageAverage((int64_t)src, width, height);
	float dstAverage = ImageAverage((int64_t)dst, width, height);
	printf("\nDLL ret = %d; src_mean = %f, dst_mean = %f\n", ret, srcAverage, dstAverage);
	if (testAverage == srcAverage) printf("Test passed"); else printf("Test failed");
		
	free(src);
	free(dst);
	
	return 0;
}

Something like this. Just an idea...

 

Message 12 of 47
(307 Views)

Hi Andrey,

 

To clarify, I'd like the DLL to be called with their language of choice. (C, C++, Python, etc.) I will look into the MoveBlock functions which you have referred to, thank you!

0 Kudos
Message 13 of 47
(299 Views)

@Gregory wrote:

Hi Andrey,

 

To clarify, I'd like the DLL to be called with their language of choice. (C, C++, Python, etc.) I will look into the MoveBlock functions which you have referred to, thank you!


OK, then I really recommend do not put LabVIEW Types into API Interface and keep everything as simple as possible.

This is for example, how ImageAverage can be called from Python Script:

import os
import ctypes

# Get the directory of the current script
script_dir = os.path.dirname(os.path.abspath(__file__))

# Construct the full path to the DLL
dll_path = os.path.join(script_dir, 'SharedLib.dll')

# Load the DLL
dll = ctypes.CDLL(dll_path)

# Define the function prototype
dll.ImageAverage.argtypes = [ctypes.POINTER(ctypes.c_ushort), ctypes.c_int, ctypes.c_int]
dll.ImageAverage.restype = ctypes.c_float

# Define the image dimensions
width = 320
height = 240

# Create an array of unsigned short values filled with a constant value (e.g., 1000)
image_data = (ctypes.c_ushort * (width * height))(*[1000] * (width * height))

# Call the ImageAverage function from the LabVIEW DLL
src_average = dll.ImageAverage(image_data, width, height)

print(f"The average value of the image is: {src_average}")

Demo project in the attachment.

0 Kudos
Message 14 of 47
(292 Views)

Hi Andrey, thank you so much for the help. I have a few questions:

 

1. On the LabVIEW side, you have a call library function node which calls a library named "LabVIEW" and the function is "MoveBlock". Is there somewhere I can find more documentation about this library and function?

 

2. In the Python code it looks like you're passing in the full image data. But, I guess since you've defined the argtypes as a POINTER, Python knows to pass in a pointer rather than the actual data, is that correct?

0 Kudos
Message 15 of 47
(279 Views)

@Gregory wrote:

Hi Andrey, thank you so much for the help. I have a few questions:

 

1. On the LabVIEW side, you have a call library function node which calls a library named "LabVIEW" and the function is "MoveBlock". Is there somewhere I can find more documentation about this library and function?

 

2. In the Python code it looks like you're passing in the full image data. But, I guess since you've defined the argtypes as a POINTER, Python knows to pass in a pointer rather than the actual data, is that correct?


NI Doc: the MoveBlock (LabVIEW Manager Function).

Python: How can I pass an array to shared library(.dll) written in c using python.Don't remember, where the docs, probably somewhere here: ctypes Arrays and Pointers.

Message 16 of 47
(273 Views)

@Quiztus2 wrote:


@Greg
IMHO: The approach of using a Cluster doesn't bring you any advantage, since your 1D array also has it's length allocated just in front of it.

See 'Uint16Array1Base' in your header:

 


Good point, I think when the 1D array was outside the cluster / structure it was a native c-type. I'll have to test again when I'm at my development computer tomorrow.

0 Kudos
Message 17 of 47
(256 Views)

Thanks Andrey. So when a user is using C or Python ctypes to create a 2D array, is the array guaranteed to be in a continuous space of memory? 

0 Kudos
Message 18 of 47
(254 Views)

@Gregory wrote:

Thanks Andrey. So when a user is using C or Python ctypes to create a 2D array, is the array guaranteed to be in a continuous space of memory? 


Yes, they are fundamentally contiguous. In theory, the layout of the 2D array in memory depends on the ordering used, which can be either row-major or column-major, but column-major ordering is rarely used (in Fortran, as far as I remember). Also, for 16-bit images, they could be stored as big-endian or little-endian, but by default on Windows at least, they matched perfectly between C, Python, and LabVIEW.For high-performance image processing, alignment gaps can be introduced (meaning each row will start at an aligned address, by default nowadays aligned to a 64-byte boundary). Then, together with the ImageWidth, you will have a "LineWidth", but this is a special case. IMAQ Vision, as well as Intel Performance Primitives, will allocate image data in this manner, but OpenCV will allocate continuous memory by default. This is also not your case.

Message 19 of 47
(243 Views)

Unfortunately I was totally unaware of the LABVIEW MANAGER FUNCTIONs. Thank you for sharing. 

 

 

0 Kudos
Message 20 of 47
(266 Views)