

JGMS
-
Content Count
49 -
Joined
-
Last visited
Posts posted by JGMS
-
-
Thanks @Atilla,
I will add the unit to my repository. It may be of future use. -
Problem solved: There is one more action required when switching to Win64, i.e. "Enable SKIA" in the Projects window of the IDE.
Good to know for all. SKIA is fantastic! -
I traced back that the problem is caused by the use of the SKIA library.
When I create and run a new Win64 multidevice application with SKIA enabled as shown here below the error occurs. Win32 runs OK.
program Project1; uses System.StartUpCopy, FMX.Forms, SKIA, SKIA.FMX, Unit1 in 'Unit1.pas' {Form1}; {$R *.res} begin GlobalUseSkia := TRUE; Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end.
I have updated SKIA to the version 5.0, but this did not solve the problem.
Any ideas about the cause of this?
-
Thank you @Sherlock. I followed the links you posted, but it did not help me any further.
In Delphi 11.3 FMX the common Windows components have hints, except for the menuitems.
MenuItems do have the Hint, ShowHint and the HitTest property too, but these are not visible in the Object Inspector.I just tried the following to assign the names of the menuitems to the text of their hints:
For var I : Integer := 0 to Form1.ComponentCount -1 DO if Form1.components[I] is TMenuItem then begin (Form1.components[I] as TMenuItem).Hittest := True; (Form1.components[I] as TMenuItem).ShowHint := True; (Form1.components[I] as TMenuItem).Hint := (Form1.components[I] as TMenuItem).name; end;
This seems to work, though not in the right way: only the topline mainmenu items invoke hints.
Having a mainmenu with items, like "File", "Options", an so on, hoving over them with the mouse shows the names of the sub-menuitems in "Files". Rather weird, isn't it?
Has anybody idea's how to get correct hints for (sub-)menuitems?
-
Hi @Eric,
Did you manage to solve the issue altogether?
I face the same problem in Delphi 11.3. At least: the same "call stack" listing appears as above in your initial post.
In Win32 mode my FMX application (single unit, simple form, a menu and some speedbuttons) runs normally, but when switched to Win64 it fails.
In ...source/rtl/system.pas the debug break is in:
function _IsClass(const Child: TObject; Parent: TClass): Boolean; begin Result := (Child <> nil) and Child.InheritsFrom(Parent); end;
The error is in "Parent". Thereafter the error "...c0000005 ACCES_VIOLATION..." appears. Any ideas how to solve this?
-
A menuitem in Delphi (11.3 Enterprise) FMX has no "hint" property by default.
How can I add the Hint and Showhint properties to the menuitem class to show them in the Object Inspector? -
Thank you again @pyscripter,
I just stared at Demo 33 code for quite a while. Indeed, it does not look trivial. It needs a deep dive.
-
Thank you so much, @PyScripter.
The error message indeed disappeared, and the code functioned like designed.
However, the modifications envoked a new problem: it appears no longer possible to run the P4D routines in the background.
If I try to do so, I got the error "...raised exception class $C0000005 with message 'c0000005 ACCESS_VIOLATION".
Not only this occurs with the routine "Picture_Matching_using_Python" as shown above, but just with all of them if in background.
I use the following calling code:
TTask.Run( Procedure begin PyForm.Picture_Matching_using_Python(StringWithFilenames_CSV,MatchedList_Tarray, TextFileNameContainingMatchResults, some_info_Str, minimumscore_int ) ; TThread.Synchronize(nil, procedure begin ForceDirectories(NameCopyToFolder); for var j := 0 to length(MatchedList_Tarray) -1 do if MatchedList_Tarray[j] <> OriginalsList[j] then TFile.Copy(trim(MatchedList_Tarray[j]), NameCopyToFolder + trim(ExtractFileName(MatchedList_Tarray[j])), TRUE ) ; end); end);
What can be the cause of this?
I do hope you have suggestions on how to solve the issue. -
I tried your suggestion, by adding "PythonEngine1.UseWindowsConsole:= True;" in the formcreate function, just before the LoadDLL command.
The only effect was that a black command screen appeared for about half a second. The error message remained unchanged.
-
Thank you, and yes indeed.
I am not sure whether I understand about "sys.version" and "sys.path". Where can I find these?
I successfully use the following formcreate function in all my P4D projects, except for the line with "SetPythonHome", as I have added just now.
The added line does not make any difference: the same error emerges.
procedure TPyForm.FormCreate(Sender: TObject); begin MaskFPUExceptions(True); PythonEngine1 := TPythonEngine.Create(PyForm); PythonEngine1.RegVersion := '3.10'; PythonEngine1.DllName := 'python310.dll'; PythonEngine1.DllPath := 'C:\Users\myInlogName\AppData\Local\Programs\Python\Python310'; PythonEngine1.AutoLoad := false; PythonEngine1.AutoFinalize := true; PythonEngine1.AutoUnload := true; PythonEngine1.UseLastKnownVersion := false; PythonEngine1.RedirectIO := false; PythonDelphiVar1.Engine := PythonEngine1; PythonDelphiVar2.Engine := PythonEngine1; PythonDelphiVar3.Engine := PythonEngine1; PythonEngine1.SetPythonHome('C:\Users\myInlogName\AppData\Local\Programs\Python\Python310'); // I added this suggested code line here, in absence of a BeforeLoad event. PythonEngine1.loadDLL; PyEmbeddedResEnvironment3101.pythonEngine := PythonEngine1; PyEmbeddedResEnvironment3101.Autoload := True; end;
-
I found some nice Python code on WWW.SBERT.NET that perfectly suited my need to find the best matching images in two file sets.
The file sets are related to each other: they origin the same celluloids, but were created with different quality of scanning equipment. In addition, cropping and tilting may have taken place, and in different ways.
I modified the example code to create a file with information on the best fitting high-quality-version of the low-Q image, as well as the three top score values.Here is the code that runs perfectly in Python, version 3.10. I am using PyScripter: great!
You can easily test it with your own files, though it may require a series of Python modules to be installed before it runs.from sentence_transformers import SentenceTransformer, util from PIL import Image model = SentenceTransformer("clip-ViT-B-32") f = open("E:/Fotos/testmap/ListMatchingFotos.txt", "a") lijst = ["E:/Fotos/File_nr1.JPG","E:/Fotos/File_nr2.JPG"] # this is the list of low quality images. image_names = ["M:/Fotos/negatieven 001.jpg","M:/Fotos/negatieven 002.jpg","M:/Fotos/negatieven 003.jpg","M:/Fotos/negatieven 004.jpg","M:/Fotos/negatieven 005.jpg","M:/Fotos/negatieven 006.jpg","M:/Fotos/negatieven 007.jpg","M:/Fotos/negatieven 008.jpg"] image_names += lijst encoded_image = model.encode([Image.open(filepath) for filepath in image_names], batch_size=128, convert_to_tensor=True, show_progress_bar=False) processed_images = util.paraphrase_mining_embeddings(encoded_image) threshold = 0.99 near_duplicates = [image for image in processed_images if image[0] < threshold] L = len(near_duplicates) for j in range(len(lijst)): # narrow the list of pairs to consider only the files in the "Lijst" searchresults = [] for i in range(0,L): score, image_id1, image_id2 = near_duplicates[i] idf = image_names.index(lijst[j]) if (( (image_names[image_id1] == image_names[idf] ) and (image_id2 != idf) ) and (not (image_names[image_id2] in lijst))) or (( (image_names[image_id2] == image_names[idf] ) and (image_id1 != idf) ) and (not (image_names[image_id1] in lijst))): searchresults.append( near_duplicates[i] ) ls = len(searchresults) score1 = 0 score2 = 0 score, image_id1, image_id2 = searchresults[0] if ls > 1: score1, image_id11, image_id21 = searchresults[1] if ls > 2: score2, image_id12, image_id22 = searchresults[2] if image_id1 != idf: image_id2 = image_id1 if score < 85/100: f.write( image_names[idf] + " " + image_names[image_id2] + " Score1: {:.3f}%".format(score * 100) + " Score2: {:.3f}%".format(score1 * 100) + " Score3: {:.3f}%".format(score2 * 100) + str(" NO MATCH OR VERY POOR \n")) else: f.write( image_names[idf] + " " + image_names[image_id2] + " Score1: {:.3f}%".format(score * 100) + " Score2: {:.3f}%".format(score1 * 100) + " Score3: {:.3f}%\n".format(score2 * 100)) f.close()
However, the very same code doesn't run in Python4Delphi, although it uses the same PythonEngine.dll and path and libraries.
I got the error message "Project .... raised exception class EPyAttributeError with message 'AttributeError: 'NoneType' object has no attribute 'flush'".
The error is generated in the very first line "from sentence_transformers import...", on both modules, either combined or in separate lines.Here is my delphi version of the code above.
Function TPyForm.Picture_Matching_using_Python(Foto_bestanden : String; VAR Zoeklijst :TArray<string>; TekstBestand :String; TargetScore : integer) : Boolean; VAR Mem :TStringList; Lijst, BeterLijst: String; Fotos : Tarray<String>; begin If Foto_bestanden = '' then exit; Fotos := Foto_bestanden.Split([',']); Lijst := '['; BeterLijst := '['; for Var Bestand : String in Fotos DO Lijst := Lijst + '"' + B2F(Bestand) + '"' + ','; Lijst := copy(Lijst,1,length(Lijst)-1)+ ']'; for Var Bestand : String in Zoeklijst DO BeterLijst := BeterLijst + '"' + B2F(Bestand) + '"' + ','; BeterLijst := copy(BeterLijst,1,length(BeterLijst)-1)+']'; TRY Mem := TStringList.Create; With Mem DO begin Add('import os'); Add('from PIL import Image'); Add(' from sentence_transformers import SentenceTransformer, util'); Add('model = SentenceTransformer("clip-ViT-B-32") '); Add('f = open("' + B2F(TekstBestand) + '", "a")'); Add('lijst = '+ Lijst ); Add('image_names = '+ BeterLijst ); Add('image_names += lijst'); Add('encoded_image = model.encode([Image.open(filepath) for filepath in image_names], batch_size=128, convert_to_tensor=True, show_progress_bar=False)'); Add('processed_images = util.paraphrase_mining_embeddings(encoded_image)'); Add('threshold = 99/100'); Add('near_duplicates = [image for image in processed_images if image[0] < threshold] '); Add('l = len(near_duplicates) '); Add('for j in range(len(lijst)): '); Add(' searchresults = [] '); Add(' for i in range(0,l): '); Add(' score, image_id1, image_id2 = near_duplicates[i] '); Add(' idf = image_names.index(lijst[j]) '); Add(' if (( (image_names[image_id1] == image_names[idf] ) and (image_id2 != idf) ) and (not (image_names[image_id2] in lijst))) or ' + ' (( (image_names[image_id2] == image_names[idf] ) and (image_id1 != idf) ) and (not (image_names[image_id1] in lijst))): '); Add(' searchresults.append( near_duplicates[i] ) '); Add(' ls = len(searchresults) '); Add(' score1 = 0' ); Add(' score2 = 0' ); Add(' score, image_id1, image_id2 = searchresults[0]'); Add(' if ls > 1: score1, image_id11, image_id21 = searchresults[1] '); Add(' if ls > 2: score2, image_id12, image_id22 = searchresults[2] '); Add(' if image_id1 != idf: image_id2 = image_id1'); Add(' if score < ' + TargetScore.tostring + '/100: '); Add(' f.write( image_names[idf] + " " + image_names[image_id2] + " Score1: {:.3f}%".format(score * 100) + " Score2: {:.3f}%".format(score1 * 100) + " Score3: {:.3f}%".format(score2 * 100) + str(" GEEN OF TWIJFELACHTIGE MATCH \n"))'); Add(' else:'); Add(' f.write( image_names[idf] + " " + image_names[image_id2] + " Score1: {:.3f}%".format(score * 100) + " Score2: {:.3f}%".format(score1 * 100) + " Score3: {:.3f}%\n".format(score2 * 100))' ); Add('f.close() '); end; TRY Result := True; PythonEngine1.ExecString( ansiString( Mem.text ) ); Except Result := False; END; FINALLY Mem.Free; END; end;
I have no idea how to proceed, and do hope that anyone does.I would very much appreciate any help.
Jan
-
On 4/21/2023 at 4:41 PM, SwiftExpat said:This covers how to set it up, after that you can run pip against your embedded dist. Note, path is important and likely why you got the same results.
https://dev.to/fpim/setting-up-python-s-windows-embeddable-distribution-properly-1081
Read through this one as well,
Hi SwiftExpat,
I created a folder with an embedded python 3.11 version in it, and installed all required libraries for my projects. Now my Delphi application(s) all have "PythonEngine1.DllPath" set to this "embedded folder".
I installed the libraries from within with the "Scripts" subfolder using commands like ".\pip install opencv-contrib-python --no-warn-script-location".
The command "python -m pip list -v" still returns the list of the libraries in the 3.10 system, though. Likely, that is due to the system's environment path setting, in where there is (and, I bet, should be) no entry added for the embedded version. I can live with that.
Next, I compressed everthing in a zip file "Python.zip", with the intention to unpack this as "3.11" aside the executable on other machines, using Inno Setup.
I can probably manage that, but after installation on the target machine the DLLPath should first be redirected to subfolder "3.11" in the path of the executable. On this one I am stuck.
Do you have tips to achieve this path change? Or do you have helpful comments on the approach I described? -
On 4/23/2023 at 1:21 AM, KoRiF said:You have to install every of dcl***.dpk
But first you must install foundation libraries as said there:- PythonEnviroments allows effectively manage [embedded] Python environments
- Lightweight-Python-Wrappers allows easily wrap any python library as Delphi component and easily use it in any Object Pascal project
Read this for more concept understanding
Thank you KoRiF,
Being very unfamiliar with installing components, it took me quit some time to get things sorted out. I have the P4D sciences libs installed as components now. Next is getting them to work. I want OpenCV + Numpy to deal with image handling smoothly. Any tips for examples?
-
18 hours ago, KoRiF said:Try this. With proper installation of the whole stack of components, this should solve your problem automatically
Thanks for your tip. I downloaded and unpacked it. I opened the file "P4DDataSciencesComponentSuite.groupproj" in Delphi and did "Build all Projects" of the group.
This succeeded when for Windows- 32bit. Is there nothing more that I should do upfront to get all of these libraries available in P4D projects?
-
33 minutes ago, SwiftExpat said:pip list -v
Both commands result in exactly the same list, though the contents of the folders are quite different.
Can you explain how to install to an embedded list? -
After adding a new function that uses the "Numpy" and "CV2" libraries, lots of DLLs are loaded when the function is called. See "code" below.
Initially, Delphi (or Python) complained because of missing the libraries. Then I copied the Numpy folder from the Python/3.10/site-packages of the machine to the ditto folders near the program.
Apart from slowing down loading, the function fails, although it runs perfect in PyScripter.
Is copying the folders Numpy and OpenCV sufficient? Or better: when compiling goes without complaining, could anything still be missing?
Here are some of the messages, shown here in hope for that it rings a bell in anyone of you. If so, please let me know.
Thanks ahead,
jan
Module Load: libffi-7.dll. No Debug Info. Base Address: $00007FF93C590000. Process FotoPower.exe (11132) Module Load: VCOMP140.DLL. No Debug Info. Base Address: $00007FF923BC0000. Process FotoPower.exe (11132) Module Load: MSVCP140.dll. No Debug Info. Base Address: $00007FF91FA50000. Process FotoPower.exe (11132) Module Load: _check_build.cp310-win_amd64.pyd. No Debug Info. Base Address: $00007FF92F730000. Process FotoPower.exe (11132) onecore\com\combase\dcomrem\resolver.cxx(2414)\combase.dll!00007FF93FCA6FFA: (caller: 00007FF93FC0B9F8) ReturnHr(58) tid(2dc0) 80070005 Toegang geweigerd. Module Load: libopenblas64__v0.3.21-gcc_10_3_0.dll. Has Debug Info. Base Address: $00007FF8E9DA0000. Process FotoPower.exe (11132) Module Load: _multiarray_umath.cp310-win_amd64.pyd. No Debug Info. Base Address: $00007FF8F19D0000. Process FotoPower.exe (11132) Module Load: _multiarray_tests.cp310-win_amd64.pyd. No Debug Info. Base Address: $00007FF929EA0000. Process FotoPower.exe (11132) Module Load: _umath_linalg.cp310-win_amd64.pyd. No Debug Info. Base Address: $00007FF923BA0000. Process FotoPower.exe (11132) Module Load: _pocketfft_internal.cp310-win_amd64.pyd. No Debug Info. Base Address: $00007FF922860000. Process FotoPower.exe (11132) Module Load: mtrand.cp310-win_amd64.pyd. No Debug Info. Base Address: $00007FF9197E0000. Process FotoPower.exe (11132) Module Load: bit_generator.cp310-win_amd64.pyd. No Debug Info. Base Address: $00007FF921870000. Process FotoPower.exe (11132) Module Load: _common.cp310-win_amd64.pyd. No Debug Info. Base Address: $00007FF9210D0000. Process FotoPower.exe (11132) Module Load: _hashlib.pyd. No Debug Info. Base Address: $00007FF9221F0000. Process FotoPower.exe (11132) Module Load: libcrypto-1_1.dll. No Debug Info. Base Address: $00007FF8F0440000. Process FotoPower.exe (11132) Module Load: _bounded_integers.cp310-win_amd64.pyd. No Debug Info. Base Address: $00007FF9201C0000. Process FotoPower.exe (11132)
-
@SwiftExpat
After compiling, each of the "P4D-embedded" applications get their own python subfolder (like "3.10" in my case) within the directory of the executable. Considering disk space, using the same "PythonEngine1.DllPath" for all should be more efficient. How does this affect the installation of the program on other machines? I presume that the DLLPath should also be made available on the target machine, which may look a bit of weird.
-
@KoRiFIndeed, that is exactly the case. Python and all required libraries come together with the executable in an associated folder "3.10". In my understanding this folder "3.10" (in my case) must be copied to the same directory as of the Delphi executable in the target machine, although "PyEmbedded" hints me to having it all inside the executable. Might still be possible, I hope.
Anyway, there is no need to install Python on the target machine. Moreover, it works like a charm!
-
1
-
-
@SwiftExpatThat was the perfect answer! Thank you very much, indeed.
Before starting the program I removed the Environment path referencing the installed Python version.
I was happy to see that the program runs independently. Just like I want it.
I added "PythonEngine1.DllPath := IncludeTrailingPathDelimiter(ExtractFileDir(Application.ExeName)) + PythonEngine1.RegVersion;" to the code.
Great.
-
Thanks for your reply @KoRif,
I found out that my (VCL) programs use the installed version on the target machine, rather than the embedded version of Python in the program. The debug-version of the executable showed errors that libraries were missing. These libraries were included in the side-packages subfolder associated with the executable, but were absent in the Python folder of the machine. Apparently, there is no embedding!
Thus the question remains: what do I wrong? I use the following code for the creation of the Python form:
procedure TPyForm.FormCreate(Sender: TObject); begin MaskFPUExceptions(True); PythonEngine1 := TPythonEngine.Create(PyForm); PythonEngine1.RegVersion := '3.10'; PythonEngine1.DllName := 'python310.dll'; PythonEngine1.AutoLoad := false; PythonEngine1.AutoFinalize := true; PythonEngine1.AutoUnload := true; PythonEngine1.UseLastKnownVersion := false; PythonEngine1.RedirectIO := false; PythonEngine1.loadDLL; PyEmbeddedResEnvironment3101.pythonEngine := PythonEngine1; PyEmbeddedResEnvironment3101.Autoload := True; end;
When the project is compiled the folder 3.10 is created or updated, with all required libraries in it.
An installation program copies everything that is needed to the target machine. So far so good, I'd presume.
What more is needed? Anything specific in the registry settings, like "Environment Variables"?
-
My Delphi programs with the P4D-component PyEmbeddedResEnvironment310 run like a charm, as long as I keep them on my own machine (W11 + Delphi 11.3 Enterprise).
On the target machines (W10), everything looks good: the embedded "3.10" environment is exactly the same, and contains all the required python libraries.
Besides, I have the registry key "Computer\HKEY_CURRENT_USER\Software\Embarcadero\BDS\22.0\Environment Variables" adjusted through the batch file: "reg_env.bat", from P4D github.
In there, "PYTHONENVIRONMENTDIR" reads "C:\Program Files\MyProgram", where the folder "3.10" stands aside the program executable (I also tried "C:\Program Files\MyProgram\3.10" by the way).
Note that the programs work as normal, except for that the Python scripts don't work. No errors occur. Just no effects.
What is it what I do wrong?
Many thanks ahead.
Jan
-
Thank you so much!
I am so sorry that I did not recently look at this forum!
It works now! Unbelievable.
P4D is even greater now!
Regards, Jan
-
1
-
-
P4D is great! I use it with pleasure, backboned by PyScripter, great as well!) to test my code beforehand.
The following Python code works perfect in PyScripter, but not in P4D in Delphi 11.2 or 11.3
import cv2 import imutils image = cv2.imread("E:/APPS/ScannedFotos/DCIM/100MEDIA/PICT0022.JPG") image_rotated = imutils.rotate(image, 2) cv2.imwrite("E:/APPS/ScannedFotos/DCIM/100MEDIA/PICT0022.JPG", image_rotated, [int(cv2.IMWRITE_JPEG_QUALITY), 80])
In Delphi (VCL) I have have a form with a TPyEmbeddedResEnvironment310. All required Python libraries are present in de 3.10 folder within the Win64 subfolder of the Delphi code. (numpy, cv2, imutils, etc...)
The PythonEngine is loaded in the formcreation routine like so:
procedure TPyForm.FormCreate(Sender: TObject); begin PythonEngine1 := TPythonEngine.Create(PyForm); PythonEngine1.RegVersion := '3.10'; PythonEngine1.DllName := 'python310.dll'; PythonEngine1.AutoLoad := false; PythonEngine1.AutoFinalize := true; PythonEngine1.AutoUnload := true; PythonEngine1.UseLastKnownVersion := false; PythonEngine1.RedirectIO := false; PythonEngine1.loadDLL; PyEmbeddedResEnvironment3101.pythonEngine := PythonEngine1; PyEmbeddedResEnvironment3101.Autoload := True; end;
The function that should work (like many others I have created so far):
Function TPyForm.Rotate_Plaatje_Met_CV2(Foto_bestand : String; Hoek : Double) : Boolean; VAR Mem :TStringList; begin Foto_bestand := B2F(Foto_bestand) ; TRY Mem := TStringList.Create; if FileExists(Foto_Bestand) then With Mem DO begin Add( 'import cv2 ' ); Add( 'import imutils' ); Add( 'image = cv2.imread("' + Foto_bestand + '")'); Add( 'i = ' + Hoek.ToString ); Add( 'image_rotated = imutils.rotate(image, i) ' ); Add( 'cv2.imwrite("'+ Foto_bestand + '", image_rotated, [int(cv2.IMWRITE_JPEG_QUALITY), 80]) '); end; TRY PythonEngine1.ExecString( ansiString( Mem.text ) ); Result := True; Except Result := False; END; FINALLY Mem.Free; END; end;
I have no idea what is wrong. I tried lots of different ways with CV2 codes found on the internet, but all without success. I keep ketting the messages:
Project ....exe raised exception class $C000008E with message 'c000008e FLOAT_DIVIDE_BY_ZERO'. and when trying once more directly therafter: Project ....exe raised exception class EPyAttributeError with message 'AttributeError: partially initialized module 'cv2' has no attribute 'INTER_AREA' (most likely due to a circular import)'.
I hope anybody can help me out of this circle. Because it just runs in PyScripter, it must be solvable, isn't it?
Many thanks ahead.
Jan
Howto use P4D Demo29 with PIL and OpenCV
in Python4Delphi
Posted
I try to use P4D Demo 29 as the basis for image handling functions.
The demo code works like a charm for PIL-based functions, but I can't get it to deal with CV2 (OpenCV) functions. I added the conversions of the PIL- to a CV2-image (OpenCV) and the reverse after processing, but apparently, I missed a clue.
In the mainform there is a Timage (image1) that is passed as a Var parameter to the gamma correction function:
PyForm.PyGammaCorrection( Image1, gammasetting ); {e.g. range between 0.05 and 2}
I have prepared and successfully tested all Python code in PyScripter.👌
Hence the problem is in the Delphi part of the routine.
Here is my code:
function TPyForm.ImageToPyBytes(AGraphic : TGraphic) : Variant; var _stream : TMemoryStream; _bytes : PPyObject; begin _stream := TMemoryStream.Create(); try AGraphic.SaveToStream(_stream); _bytes := GetPythonEngine.PyBytes_FromStringAndSize( _stream.Memory, _stream.Size ); Result := VarPythonCreate(_bytes); GetPythonEngine.Py_DECREF(_bytes); finally _stream.Free; end; end; ///////////////////////////////////////////////////////////////////////// procedure TPyForm.PyGammaCorrection(VAR Image1 : Timage; Setting: double ); var _im : Variant; _stream : TMemoryStream; pargs: PPyObject; presult :PPyObject; P : PAnsiChar; Len : NativeInt; PythonCode : TstringList; begin TRY PythonCode := TstringList.Create; With PythonCode DO begin Add('from PIL import Image' ); Add('import cv2 '); Add('import numpy as np '); Add('from io import BytesIO' ); Add('def adjust_gamma(image, gamma=1.0): '); Add(' invGamma = 1.0 / gamma '); Add(' table = np.array([((i / 255.0) ** invGamma) * 255 for i in np.arange(0, 256)]).astype("uint8") '); Add(' return cv2.LUT(image, table) '); Add('def ImageToBytes(image):' ); Add(' stream = BytesIO()' ); Add(' image.save(stream, image.format)' ); Add(' return stream.getvalue()'); Add('def get_opencv_img_from_buffer(buffer, flags): '); Add(' bytes_as_np_array = np.frombuffer(buffer.read(), dtype=np.uint8) '); Add(' return cv2.imdecode(bytes_as_np_array, flags) '); Add('def ProcessImage(data):'); Add(' img = get_opencv_img_from_buffer(BytesIO(data), cv2.COLOR_RGB2BGR) '); Add(' gamma = ' + Setting.ToString ); Add(' new_im = Image.fromarray( cv2.cvtColor( adjust_gamma(img, gamma), cv2.COLOR_BGR2RGB ) )' ); Add(' return new_im'); end; GetPythonEngine.ExecStrings(PythonCode); _im := MainModule.ProcessImage(ImageToPyBytes(Image1.Picture.Graphic)); // We have to call PyString_AsStringAndSize because the image may contain zeros (P4D Demo 29) with GetPythonEngine do begin pargs := MakePyTuple( [ExtractPythonObjectFrom(_im)] ); try presult := PyObject_Call( ExtractPythonObjectFrom(MainModule.ImageToBytes), pargs, nil); // <<===================== problem causing code line. What about "MainModule.ImageToBytes"? try if PyBytes_AsStringAndSize(presult, P, Len) < 0 then begin // ShowMessage('This does not work and needs fixing'); Abort; end; _stream := TMemoryStream.Create(); try _stream.Write(P^, Len); _stream.Position := 0; Image1.Picture.Graphic.LoadFromStream(_stream); finally _stream.Free; end; finally Py_XDECREF(pResult); end; finally Py_DECREF(pargs); end; end; Finally PythonCode.Free; END; end;
The error occurs in the line with "PyEval_CallObjectWithKeywords" (or like in the code with the equivalent "PyObject_Call" instead)
the resulting image ("_im: in the code) shows '<PIL.Image.Image image mode=RGB size=4928x3264 at 0x19B16B45BB0>' when inspected, which is OK.
"pargs" shows $19B0845BD00, which to me looks at least better than "nil", but "presult" does show "nil", thus what causes the program to Abort.
Can anybody please tell me what causes the problem and how to get it to work?
Many thanks,
Jan