What's the best way to handle interaction with this C Lib?

We’ve hired someone to create a C interface to OpenCV 4.5.2, since the built-in C API is now deprecated. He has provided an initial test DLL and Dylib, and some simple C code based on python scripts I sent him that I had been working with: open, resize, change the color space, run canny edge detection.

I’m getting a little outside of my comfort zone here and keep getting tripped up on what’s probably something fairly simple. Right now I just want to test that his code loads images into Xojo. Open CV uses the CVMat struct for passing around image data. So the code I’ve been given requires me to create a Mat (basically an empty image), which I can then fill with the results of OpenCV function calls, and then to destroy that Mat when I’m done.

I’ve created a Mat class, with external methods for CVMatCreate, CVMatFree, CVMatHeight, CVMatWidth, CVResize, CVimread (from the C DLL he made). Corresponding internal methods are createMat, freeMat, getHeight, getWidth, resize, loadImage, respectively.

All of this is working - loading up the DLL, no errors calling the OpenCV functions. As a testbed, he provided a simple C application that does the same thing as my python scripts. So the python code:

img = cv.imread("Test-Pattern.tif")

becomes (in C):

CVMat image = CVimread("Test-Pattern.tif");
	if (image != NULL) {
		printf("Image loaded.\n");
		CVMatFree(image);
	} else {
		printf("Error loading image.\n");
		return 0;
	}

Where I’m getting confused is with the creation of the CVMat struct, in Xojo. The C code for that is:

(from grdOpenCV.cpp)

CVMat CVMatCreate(void)
{
	try {
		cv::Mat *mat = new cv::Mat();
		return (CVMat)mat;
	} catch (...) { }

	return NULL;
}

(from grdOpenCV.h):

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

// exported opencv types
typedef void*	CVMat;

// exported opencv type functions
CVMat CVMatCreate(void);

How should I go about setting this up in Xojo? Based on my reading of the OpenCV docs linked to above, a CVMat can become a pretty complex beast, that’s sometimes very simple, and sometimes not at all. I’d be happy to PM a link to the project file with the C Code and DLL if someone is able to help me puzzle through this. Once I get past this hurdle, the rest seems to be fairly straightforward. I just can’t seem to wrap my head around this one.

Thanks

CVMatCreate is allocating the memory for the structure and then returning a pointer to that structure (hence the need for a separate CVMatFree function.) If the Xojo code never needs to access/modify the structure then you can just refer to it as a Ptr in Xojo.

Declare Function CVMatCreate Lib "TheDLL.dll" () As Ptr
' get a Ptr to the C structure.
Dim ref As Ptr = CVMatCreate()

If, on the other hand, you need to access/modify members of the structure then you’d use the code above and then convert the Ptr into something more useful.

Such as a MemoryBlock:

Dim struct As MemoryBlock = ref
struct.UInt32Value(123) = 4 ' modify

Or as a Structure you have created in the Xojo IDE which exactly matches the binary layout of the DLL’s structure:

' Ptr.StructureName converts the Ptr into the named Xojo structure type
Dim struct As MyXojoCVMat = ref.MyXojoCVMat
struct.MemberName = 4 ' modify

Or as a copy of the structure:

' Ptr.StructureName(Offset) copies the structure
Dim struct As MyXojoCVMat = ref.MyXojoCVMat(0) ' offset=0
struct.MemberName = 4 ' modify the local copy

Or perhaps something else depending on exactly how CVMat is used.

2 Likes

Thanks.

I redid everything this morning. Moved all the CVMat stuff into a new OCV_Mat class that only deals with the Mat itself. Its constructor sets up the mat, and it has two methods for getting the width and height via the C API. Otherwise, the Mat’s structure is handled by the API and OpenCV.

So the following code works:

var f as FolderItem  = new FolderItem("C:\Users\perry\Documents\Temp-Xojo-Code\Support-Files\Test-Pattern.tif")
var path as cstring = f.NativePath.ToText.ToCString(Xojo.Core.TextEncoding.UTF8)

//create a new CVMat
var  source as new OCV_Mat

//load the image into that CVMat
source.cvmat = loadImage(path)

var w, h as int32  //this reports back the correct dimensions, so we have an image
w = source.getWidth
h = source.getHeight


//Create a new CVMat to hold a smaller image
var  smaller as new OCV_Mat

//resize 
resize(source.cvmat, smaller.cvmat, Canvas1.Width, Canvas1.Height)

var smW, smH as int32  //this reports back the correct dimensions, so scaling works
smW = smaller.getWidth
smH = smaller.getHeight