Jump to content
BennieC

Returning lists from Python to Delphi

Recommended Posts

Hi,

I have a Delphi application that calls OpenCV via Python4Delphi.  It returns a tuple of floating point values in Python.

However when I return the tuple to Delphi I get a string variable.  Is there a way to force the conversion to maintain the floating point values.  I managed to parse the string list in Delphi into a floating point array but the application should be in real time so this is an unnecessary step.

In Python

image.thumb.png.f797306729a6ecfb9031b41e36159b19.png

In Delphi

image.thumb.png.0a544403af03b305221bc0bc5e7c8a70.png

Regards

Share this post


Link to post

Thank you for this - it does seem to be the right way to go as my current string parsing is far too slow.  However I have no idea how to use either of the two options.

In the Buffer protocol, how do I return my result which seems to be a list of arrays.  at present the return value is just the tuple which in Python looks like this:

Name: layerOutputs

Type: tuple

Value: (array([[0.09, 0.03, 0.34...0.]]. dtype=float32), array([[0.06, 0.05, 0.67...0.]]. dtype=float32))

Currently this is just returned as a value with name layerOutputs, but Delphi sees this as a character string

 

In the MemShare solution - how do I determine the memory position in Python and how do I determine the memory structure to use the binary representation.

 

Are there any resources you can point me to as I am at a complete loss/

Share this post


Link to post
On 8/11/2022 at 6:28 AM, BennieC said:

the application should be in real time so this is an unnecessary step.

I can see you want to optimize this, but where is the real bottleneck that prevents you doing this in real time?

 

What part is slow that is visible to the user?

On 8/11/2022 at 6:28 AM, BennieC said:

I managed to parse the string list

Post this code, some people might give you some optimizations.

 

Always a minimal working sample to discuss will help you get better answers.

Share this post


Link to post
2 hours ago, BennieC said:

Value: (array([[0.09, 0.03, 0.34...0.]]. dtype=float32), array([[0.06, 0.05, 0.67...0.]]. dtype=float32))

Currently this is just returned as a value with name layerOutputs, but Delphi sees this as a character string

No it does not.   You get a tuple containing arrays of floating type values.   Are you using VarPyth? Is layerOutputs a Variant?

If yes then you can use:

 

var arr:Variant := layerOutputs.GetItem(0);  // the first item of the tupple (array).
var value:Variant := arr.GetItem(0);  //the first floating value of the array

 

Edited by pyscripter

Share this post


Link to post

@SwiftExpat

Thanks for the response.  Regarding the bottleneck , the processing is requires the parsing of 507 + 2028 (and possibly an additional 8000) items, each consisting of 41 values (Yolo output)  This has to happen in less than 10ms in order to stay within real time, or as close to that as possible.

Regarding the code - my current attempt looks as follows (It does get the correct values out but takes about 500ms)

      aRectItmStr := aRectList.GetItem(rectCnt); // Get a rect , should have 41 values
      // It looks like this is a single string
      // Remove unnecessary spaces, linefeeds and brackets
      // Some records have long spaces
      while containstext(aRectItmStr, '  ') do
        aRectItmStr := StringReplace(aRectItmStr, '  ', ' ', [rfReplaceAll]);    // Repeat until only single spaces exist
      aRectItmStr := StringReplace(aRectItmStr, '[', '', [rfReplaceAll]);
      aRectItmStr := StringReplace(aRectItmStr, ']', '', [rfReplaceAll]);
      aRectItmStr := StringReplace(aRectItmStr, #$A, '', [rfReplaceAll]);  // #=numerical code, $=Hexdecimal
      aListArray := aRectItmStr.Split([' ', '''']);
      for i := 0 to MIN(length(aListArray), 41)-1 do
        aRectItmVals := strtofloatdef(aListArray, 0.0);

 

I know this is probably inefficient but it would help a lot if i can just read the items as a n array of float values.

Kind regards

Share this post


Link to post

@pyscripter

Thank you.  Indeed the layerOutputs is a variant and I am using VarPyth.

However, using your syntax I seem to have made progress.  Could you please explain the syntax as I am not familiar with that.  Also, how do I declare a variable in a loop using the inline var statement?

Kind regards

 

Share this post


Link to post

@pyscripter

I have used your structure but still Delphi insist on giving me strings instead of floating point values.

image.png.b6cae72d144f79f4288f7f6bc2646a68.png

Can I somehow force the variant conversion to take this as a float?  I have tried to assign the variant to single variable but have no luck.

Regards

 

Share this post


Link to post
2 hours ago, BennieC said:

@pyscripter

I have used your structure but still Delphi insist on giving me strings instead of floating point values.

image.png.b6cae72d144f79f4288f7f6bc2646a68.png

Can I somehow force the variant conversion to take this as a float?  I have tried to assign the variant to single variable but have no luck.

Regards

 

Variants are converted to strings when shown in the debugger.  It does not mean that they contain strings.

Share this post


Link to post

@pyscripter

I have used your structure but still Delphi insist on giving me strings instead of floating point values.

image.png.b6cae72d144f79f4288f7f6bc2646a68.png

Can I somehow force the variant conversion to take this as a float?  I have tried to assign the variant to single variable but have no luck.

Regards

 

I have tried assigning the variant to a single but get no joy.  What does work is if I convert it to a single.

            var ConfValue:variant := aRectItm.GetItem(classCnt);
            ConfSingle := strtofloat(ConfValue);
 

Share this post


Link to post
On 9/9/2022 at 12:51 PM, BennieC said:

Thank you for this - it does seem to be the right way to go as my current string parsing is far too slow.  However I have no idea how to use either of the two options.

 

I'm not sure which solution will be faster and more stable for you, but given the large amount of data, you are quite able to notice the difference.


Generally speaking, these are not two different options, but two sides of the same coin.

Buffer Protocol obliges to place data in memory deterministically and sequentially
(that is, in the spirit of the buffer of a C language). Python builds some of its objects around such a buffer

Shared Memory is an operating system level concept that allows transferring access to such a buffer from one process to another

 

 

https://docs.python.org/3/library/multiprocessing.shared_memory.html

On 9/9/2022 at 12:51 PM, BennieC said:

how do I return my result which seems to be a list of arrays.  at present the return value is just the tuple

 

Support for the buffer protocol means exactly that your data displayed as a tuple is actually placed in memory in a serial buffer

https://numpy.org/doc/stable/reference/generated/numpy.ndarray.data.html

On 9/9/2022 at 12:51 PM, BennieC said:

 

In the MemShare solution - how do I determine the memory position in Python and how do I determine the memory structure to use the binary representation.

 

https://docs.python.org/3/library/array.html#array.array.buffer_info

Determining the address of the buffer in memory would be too tricky, but it is not required at all.
You have to create a shared buffer using an arbitrary string name and all you need to do is pass this identifier between Delphi and Python 

(and maybe convert to numpy https://jakevdp.github.io/blog/2014/05/05/introduction-to-the-python-buffer-protocol/#:~:text=¶,manipulate large arrays of data.)

 

here is shown example to pass data from TStringGrid to share on the Python side but common idea could be the same: 

https://github.com/KoRiF/MultyPy4Delphi/blob/master/UnitGridDataPy.pas#L608

https://github.com/KoRiF/MultyPy4Delphi/blob/master/UnitGridDataPy.pas#L561

https://github.com/KoRiF/MultyPy4Delphi/blob/master/UnitGridDataPy.pas#L333

https://github.com/KoRiF/MultyPy4Delphi/blob/master/UnitGridDataPy.pas#L666

 

 

 

 

On 9/9/2022 at 12:51 PM, BennieC said:

 

Are there any resources you can point me to as I am at a complete loss/

https://helpful.knobs-dials.com/index.php/Python_usage_notes_-_struct,_buffer,_array,_bytes,_memoryview

 

https://docs.python.org/3/library/multiprocessing.shared_memory.html

https://docs.python.org/3/c-api/memoryview.html

https://mingze-gao.com/posts/python-shared-memory-in-multiprocessing/#test-code

 

Hope, this helps

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×