Params to Python external script?

Got a LiveCode personal license? Are you a beginner, hobbyist or educator that's new to LiveCode? This forum is the place to go for help getting started. Welcome!

Moderators: FourthWorld, heatherlaine, Klaus, kevinmiller

Zax
Posts: 457
Joined: Mon May 28, 2007 10:12 am
Location: France
Contact:

Re: Params to Python external script?

Post by Zax » Tue Jul 13, 2021 12:30 pm

I'll try to retreive one of my previous test code with for each tomorrow (I'm not on the right machine at this time) and also post my test stack.

I have to say that, even if the loop is optimized and well coded, one shouldn't expect very fast processes, as LC is not designed to image processing. I as an old Hypercard user, I love LC, and ImageData is a very nice feature, but I think I really should use some language with external graphic library for this kind of project. In fact, it's a very small project and I will not use the compiled app often, so I decided to realized it in pure LC.

FourthWorld
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 9823
Joined: Sat Apr 08, 2006 7:05 am
Location: Los Angeles
Contact:

Re: Params to Python external script?

Post by FourthWorld » Tue Jul 13, 2021 2:00 pm

Zax wrote:
Tue Jul 13, 2021 12:30 pm
I have to say that, even if the loop is optimized and well coded, one shouldn't expect very fast processes, as LC is not designed to image processing.
Python isn't either, which helps make the prospect of a comparison interesting.

Python's advantage here is that it has indexed arrays, which LC Builder has, but oddly have not been added to LC Script.
Richard Gaskin
LiveCode development, training, and consulting services: Fourth World Systems
LiveCode Group on Facebook
LiveCode Group on LinkedIn

Bernard
Posts: 351
Joined: Sat Apr 08, 2006 10:14 pm
Location: London, England

Re: Params to Python external script?

Post by Bernard » Tue Jul 13, 2021 6:00 pm

FourthWorld wrote:
Tue Jul 13, 2021 2:00 pm
Zax wrote:
Tue Jul 13, 2021 12:30 pm
I have to say that, even if the loop is optimized and well coded, one shouldn't expect very fast processes, as LC is not designed to image processing.
Python isn't either, which helps make the prospect of a comparison interesting.

Python's advantage here is that it has indexed arrays, which LC Builder has, but oddly have not been added to LC Script.
Python has this: https://en.wikipedia.org/wiki/Python_Imaging_Library

So it may be that what Zax is trying to do is done in that C library (the library has pixel transparency manipulation). I'm not expecting LC to be able to do what Python can do, quite simply because so many millions of hours have gone into enhancing Python and its libraries. But given what Zax is doing, I would hope that "repeat for each" and memoization would significantly increase the speed of what he's doing.

SparkOut
Posts: 2852
Joined: Sun Sep 23, 2007 4:58 pm

Re: Params to Python external script?

Post by SparkOut » Tue Jul 13, 2021 6:37 pm

I don't know what you might find in Hermann's stacks, but he did some amazing things, with a number of image processing tools among them. Some of them, I believe, leverage imagemagick libraries and from within LiveCode could produce high-speed results.
It's definitely worth delving to see what stacks are left and appreciate his legacy whatever you might find.

Bernard
Posts: 351
Joined: Sat Apr 08, 2006 10:14 pm
Location: London, England

Re: Params to Python external script?

Post by Bernard » Tue Jul 13, 2021 8:00 pm

SparkOut wrote:
Tue Jul 13, 2021 6:37 pm
I don't know what you might find in Hermann's stacks, but he did some amazing things, with a number of image processing tools among them. Some of them, I believe, leverage imagemagick libraries and from within LiveCode could produce high-speed results.
It's definitely worth delving to see what stacks are left and appreciate his legacy whatever you might find.
Totally agree. Also Wilhelm Sanke did a lot of work with images. As did Scott Rossi.

If I was going to look at what was possible with Livecode, I'd be googling all those names and seeing what's available.

jacque
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 7228
Joined: Sat Apr 08, 2006 8:31 pm
Location: Minneapolis MN
Contact:

Re: Params to Python external script?

Post by jacque » Tue Jul 13, 2021 9:02 pm

I agree, the three names mentioned did good work with images and I vaguely remember that one of them did a grayscale conversion (but it was too long ago for me to be sure.)

At any rate, I think a lot of the slowdown is with this line inside the repeat loop:

Code: Select all

put ntcGray & ntcGray & ntcGray into char (tImgPosition + 2) to (tImgPosition + 4) of tImage
That's because it has to count from 0 to the current position every time through the loop, and as the position increases it takes longer for the count.

Instead of putting the original imagedata into tImage variable and trying to alter it, I'd put empty into the variable to start. Then for each iteration, just append the converted RGB to the end of tImage variable. This should work because you're doing the conversion in order, from the first quadruple to the last.

When tImage is fully populated, set the imageData of the destination image to tImage as you do now.
EDIT: It looks like you're already doing that with the alpha channel.
Jacqueline Landman Gay | jacque at hyperactivesw dot com
HyperActive Software | http://www.hyperactivesw.com

Zax
Posts: 457
Joined: Mon May 28, 2007 10:12 am
Location: France
Contact:

Re: Params to Python external script?

Post by Zax » Wed Jul 14, 2021 8:34 am

This is the little stack I use for testing.
TestImage.livecode.zip
(89.19 KiB) Downloaded 106 times
Result for a very small image (400 x 232 pixels) without any brightness or contrast modifications:
- current script (posted below) : repeat for... step 4 --> 590 ms
- repeat for each --> 1020 ms
- repeat for each with switch --> 1400 ms
- "after" with a brand new imageData variable : 17200 ms !

Best performance with this script:

Code: Select all

local arrParams

on preProcess
   if ("button" is in the target) then set hilite of the target to true
   set cursor to watch
   ------- reset PixEnd (delete image pour full reset si l'alphaData est corrompu)
   if (there is an image arrParams["destName"]) then delete image arrParams["destName"]
   create image arrParams["destName"]
   set threeD of image arrParams["destName"] to (the threeD of image arrParams["sourceName"])
   set borderWidth of image arrParams["destName"] to (the borderWidth of image arrParams["sourceName"])
   set showBorder of image arrParams["destName"] to (the showBorder of image arrParams["sourceName"])
   ------- visuel
   set the rect of image arrParams["destName"] to (the rect of image arrParams["sourceName"])
   set the topLeft of image arrParams["destName"] to \
         (the left of image arrParams["sourceName"] & "," & the bottom of image arrParams["sourceName"] + 6)
   -------
   put the milliseconds into fld "Timer" -- /////
end preProcess

on getParams
   put "Pix" into arrParams["sourceName"]
   put "PixEnd" into arrParams["destName"]
   put the hilite of btn "ChkInvert" into arrParams["invert"] -- négatif
   get the thumbPos of scrollbar "SliderLighten" -- de 0 (aucun éclaircissement) à 10 (éclaircissement maxi)
   put round(it * 200 / 10) into arrParams["brightness"] -- de 0 à 200
   get the thumbPos of scrollbar "SliderContrast" -- de 0 (aucune modif) à 10 (contraste maxi)
   put it / 10 into arrParams["contrast"] -- de 0 à 1
   
   put 0.30 into arrParams["xRed"] -- augmenter pour éclaircir les rouges (avec total = 1)
   put 0.59 into arrParams["xGreen"] -- augmenter pour éclaircir les verts (avec total = 1)
   put 0.11 into arrParams["xBlue"] -- augmenter pour éclaircir les bleus (avec total = 1)
   
   put the width of image arrParams["sourceName"] into arrParams["w"]
   put the height of image arrParams["sourceName"] into arrParams["h"]
end getParams

on postProcess
   ------- visual effect
   show image "PixEnd"
   put (the milliseconds - fld "Timer") && "ms" into fld "Timer"
   if ("button" is in the target) then set hilite of the target to false
end postProcess

on mouseUp
   getParams
   preProcess
   
   turnToGrayTranspa arrParams["sourceName"], arrParams["destName"], arrParams["w"], arrParams["h"], arrParams["xRed"], arrParams["xGreen"], arrParams["xBlue"], arrParams["brightness"], arrParams["contrast"], arrParams["invert"] 
   
   postProcess
end mouseUp

on turnToGrayTranspa pixSourceName, pixDestName, imgWidth, imgHeight, xRed, xGreen, xBlue, brightness, fc, invertBWboolean
   put "" into log
   addToLog log, "Button:" && the short name of me
   addToLog log, imgWidth && "x" && imgHeight && "pixels"
   addToLog log, "r = " & xRed && "  g = " & xGreen && "  b = " & xBlue
   addToLog log, "brightness = " & brightness
   
   ----------------------------------------------------- prépa : négatif
   if (invertBWboolean) then
      put 255 into grayCorrection
   else put 0 into grayCorrection
   addToLog log, "invertBWboolean = " & invertBWboolean && "  --> grayCorrection = " & grayCorrection
   
   ----------------------------------------------------- prépa : contraste
   put 0 - (fc^5 + 0.05) into fp5 -- pré-calcul
   put sqrt(fc) into racf -- pré-calcul
   addToLog log, "fc = " & fc
   
   ----------------------------------------------------- prépa : image et transparence
   put the imageData of image pixSourceName into tImage
   local aImage -- pour alphaData
   
   ----------------------------------------------------- 
   local tPixel, tImgPosition, tGray
   # repeat for all the pixels in the image
   repeat with tImgPosition = 0 to 4*((imgWidth * imgHeight) - 1) step 4
      ----------------------------------------------------- extractions des composantes RGB
      # multiply the image pixel position by 4 (Alpha + Red + Green + Blue)
      /* ============================== plus long
      # calculate the gray level
      put charToNum(char(tImgPosition + 2) of tImage) into tRed
      put charToNum(char(tImgPosition + 3) of tImage) into tGreen
      put charToNum(char(tImgPosition + 4) of tImage) into tBlue
      ----------------------------------------------------- gris
      # here you can use any grayscale formula
      # set the RGB of the pixel to the same value this gives us the gray level (0-255)
      --put (xRed * tRed) + (xGreen * tGreen) + (xBlue * tBlue) into tGray
      --------------------- application auto "négatif"
      put abs(grayCorrection - ((xRed * tRed) + (xGreen * tGreen) + (xBlue * tBlue))) into tGray
      ============================== */
      ------------ calcul valeurs gris et application auto "négatif"
      put abs(grayCorrection - ((xRed * charToNum(char(tImgPosition + 2) of tImage)) + \
            (xGreen * charToNum(char(tImgPosition + 3) of tImage)) + \
            (xBlue * charToNum(char(tImgPosition + 4) of tImage)))) into tGray
      
      ----------------------------------------------------- contraste
      if (fc > 0) then
         put (tGray * (1 - racf)) + racf * (255 / (1 + (0.9921875 * exp(fp5 * (tGray - 128))))) into tGray
         -- (x * (1 - sqrt(fc))) + sqrt(fc) * (255 / (1 + (0.9921875 * exp(-(fc^5 + 0.05) * (x - 128)))))
         /* -- plus précis mais plus long
         if (fc >=  0.5) then
            put exp(-2 * (tGray - 128) * fc^4) into tmg
            put floor(128 + 127.5 * (1 - tmg) / ( 1 + tmg)) into tGray
         else
            put exp(-2 * (tGray - 128) * 0.5^4) into tmg
            get 128 + 127.5 * (1 - tmg) / ( 1 + tmg)
            put floor(tGray * (1 - 2 * fc) + 2 * fc * it) into tGray
         end if
         */
      end if
      
      ----------------------------------------------------- éclaircissement général
      put min(255, brightness + tGray) into tGray
      
      ----------------------------------------------------- application des nouvelles valeurs de gris
      put numToChar(tGray) into ntcGray
      put ntcGray & ntcGray & ntcGray into char (tImgPosition + 2) to (tImgPosition + 4) of tImage
      
      ----------------------------------------------------- transparence toutes valeurs (alphaData)
      --------- transparence proportionnelle à la valeur de gris : plus c'est clair -> plus c'est transparent
      /*
      binaryEncode("C",0) --> transparentPixel
      binaryEncode("C",255) --> opaquePixel
      */
      put binaryEncode("C", 255 - tGray) after aImage -- 0 = transparent et 255 : opaque
   end repeat
   
   -------------------------------------------------------- affectation image dest
   set the imageData of image pixDestName to tImage
   set the alphaData of img pixDestName to aImage
   if (there is a fld "Log") then put log into fld "Log" --/////
end turnToGrayTranspa

on addToLog @log, str
   put str & return after log
end addToLog
But there is a strange thing: when commenting line 119 "put ntcGray & ntcGray & ntcGray into char (tImgPosition + 2) to (tImgPosition + 4) of tImage", process takes 13000 ms! I don't understand why not modifying the imageData takes longer!
If line 119 is commented, the script only modifies the alphaData.

And yes, we could have use of external like ImageMagick, even less powerful.

jacque
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 7228
Joined: Sat Apr 08, 2006 8:31 pm
Location: Minneapolis MN
Contact:

Re: Params to Python external script?

Post by jacque » Wed Jul 14, 2021 8:25 pm

I ran your stack with script profiling turned on. When timing only the turnToGrayscale handlers, the "put after" method was the fastest. The scripted timer doesn't isolate that handler so the variance seems to be in something else.

You can run the profiler by choosing "Start profiling scripts" from the Development menu. I did that and then ran each of the buttons, then stopped profiling. This puts up a profiler log that shows the total time for each handler as well as the time for each line of every handler. You'll also see a lot of LC back-end stuff going on, which could affect the total timing.

Note that the times are not real-time calculations because the act of profiling slows down the entire run. But it will give you relative timings and a pretty good idea of where the slowdowns occur. Since profiling is slow, I chose a very small image 128x128 px.

The summary of my runs in the profiler:

Code: Select all

button "GreyTranspa_switch" of card "Main" 	1820	ms
button "GreyTranspa_Each" of card "Main"	1533	ms
button "GreyTranspa_after" of card "Main"	749	ms
-- followed by lots of LC back-end handlers and the other handlers in the buttons
However, when I used your scripted timing, the "after" script was much slower than the others. I'm not sure where the differences are. The preProcess, postProcess, and getParams handlers seem to take almost no time at all.
Jacqueline Landman Gay | jacque at hyperactivesw dot com
HyperActive Software | http://www.hyperactivesw.com

Bernard
Posts: 351
Joined: Sat Apr 08, 2006 10:14 pm
Location: London, England

Re: Params to Python external script?

Post by Bernard » Thu Jul 15, 2021 12:11 am

Jacque, isn't the profiler only with the Business IDE?

Zax, since the loop over the pixels is where you say the speed is lost, in my opinion you need to strip out most of your processing in those loops to prove to yourself that "repeat... step 4" is faster than "repeat for each". For instance, one thing I did was at the end of your different handlers I stored the processed image as a custom property, then I could compare afterwards to see if they were the same size at the end of the processing, and they were not the same. This difference in size could be a sign that you're not doing the same processing in each variant of your handler.

In my tests on an image (testing just looping over the pixels) "repeat for each" is at least 10x faster than using the kind of repeat loop you have chosen. Strip down the various handlers to a minimum and assure yourself which is the fastest at extracting pixels and putting them into a new image, then assure yourself that the new image is the same size at the end of each handler as you add back more functionality. It seems that through two different approaches Jacque and I are getting confirmation of what we have long known to be true. Maybe something has changed in LC and "repeat for each" is not now as fast as the multi-pass repeat loop ("repeat ... step 4"). Whilst obviously your outer loop is only addressing 1 in 4 of the pixels in the imagedata to drive the loop, within the loop you then address the other pixels at least 3 times for each repetition.

Moreover, having read some of the discussions on this forum about imageData people were saying that charToNum is deprecated (in this context) and byteToNum should be used instead (they were saying that this is faster, and they are the users who specialise in image processing speeds). You'd want to use byte instead of char, too. Use the forum's search box to look for imageData and byteToNum. In many of the instances where they were discussing speed they were using the kind of repeat loop you have preferred, but their image processing didn't lend itself to a single pass the way your task does.

I hope that helps.

jacque
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 7228
Joined: Sat Apr 08, 2006 8:31 pm
Location: Minneapolis MN
Contact:

Re: Params to Python external script?

Post by jacque » Thu Jul 15, 2021 1:17 am

Jacque, isn't the profiler only with the Business IDE?
Oh. Maybe. I've had a business license so long I've lost track.
Jacqueline Landman Gay | jacque at hyperactivesw dot com
HyperActive Software | http://www.hyperactivesw.com

FourthWorld
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 9823
Joined: Sat Apr 08, 2006 7:05 am
Location: Los Angeles
Contact:

Re: Params to Python external script?

Post by FourthWorld » Thu Jul 15, 2021 2:14 am

Bernard wrote:
Tue Jul 13, 2021 6:00 pm
FourthWorld wrote:
Tue Jul 13, 2021 2:00 pm
Zax wrote:
Tue Jul 13, 2021 12:30 pm
I have to say that, even if the loop is optimized and well coded, one shouldn't expect very fast processes, as LC is not designed to image processing.
Python isn't either, which helps make the prospect of a comparison interesting.

Python's advantage here is that it has indexed arrays, which LC Builder has, but oddly have not been added to LC Script.
Python has this: https://en.wikipedia.org/wiki/Python_Imaging_Library

So it may be that what Zax is trying to do is done in that C library...
Is that library written in C, or Python?

If C, why is it only compatible with older versions of Python?
Richard Gaskin
LiveCode development, training, and consulting services: Fourth World Systems
LiveCode Group on Facebook
LiveCode Group on LinkedIn

Zax
Posts: 457
Joined: Mon May 28, 2007 10:12 am
Location: France
Contact:

Re: Params to Python external script?

Post by Zax » Thu Jul 15, 2021 7:27 am

Thank you very much for all your interesting advices, I reallu appreciate :)

I'll look deeper into my code tomorrow, and also try byte intead of char.
@Jacqueline: as I don't have the Business edition, could you please test button "GreyTranspa" of card "Main" with the Script Profiler? It's for me the fastest process.

For information, processes on MacOS compiled version take almost the same durations (I remember older LC versions where running script in IDE were slower).

Bernard
Posts: 351
Joined: Sat Apr 08, 2006 10:14 pm
Location: London, England

Re: Params to Python external script?

Post by Bernard » Thu Jul 15, 2021 11:02 am

I spent a lot of time running tests and writing the results here. When I went to post, the stupid forum software/setup demanded I login again. I did so and all my work was lost.

What kind of design trashes a user's work like that?

My tests revealed lots of contradictory results (some back up Zax claims and contradict Jacque's profiling, others back up my expectations). I really can't be bothered to spend more of my time on this when the forum is so badly designed.

Code: Select all


repeat for each ... byteToNum() 3,108 ms
repeat for each... charToNum() 3,356 ms

repeat with ... the number of chars... charToNum() 25,631 ms
repeat with ... the number of bytes in... byteToNum() 3,169 ms
Last edited by Bernard on Thu Jul 15, 2021 2:12 pm, edited 1 time in total.

Bernard
Posts: 351
Joined: Sat Apr 08, 2006 10:14 pm
Location: London, England

Re: Params to Python external script?

Post by Bernard » Thu Jul 15, 2021 2:11 pm

Is that library written in C, or Python?

If C, why is it only compatible with older versions of Python?
According to the Wikipedia page, it is C and Python.

FourthWorld
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 9823
Joined: Sat Apr 08, 2006 7:05 am
Location: Los Angeles
Contact:

Re: Params to Python external script?

Post by FourthWorld » Thu Jul 15, 2021 6:11 pm

Bernard wrote:
Thu Jul 15, 2021 2:11 pm
Is that library written in C, or Python?

If C, why is it only compatible with older versions of Python?
According to the Wikipedia page, it is C and Python.
Thanks, I see that now. If critical, the C core could be migrated to an external for LC.

But that it's C was really the main point here: Python and LC have enough in common that I would imagine both have very similar performance in areas of equivalent functionality. The only area where Python has a clear advantage is algos that benefit from indexed arrays, but while those still aren't in LC Script they are in LC Builder, so we may see good parity between the two scripting languages.
Richard Gaskin
LiveCode development, training, and consulting services: Fourth World Systems
LiveCode Group on Facebook
LiveCode Group on LinkedIn

Post Reply

Return to “Getting Started with LiveCode - Complete Beginners”