2

I'm working on a project which needs to call python script in c#. The fact is that I'm familiar with python, but not c#, not at all.

As I have learnt, there are basically two options: Iron and Python.net, you can check it out here https://www.youtube.com/watch?v=oG5mmElsWJM&lc=UgzwcXvIteCqTjmHl2N4AaABAg.9Ls9q7VkDdc9MCYIPfOW0d. I'm struggling with python.net now. I've tried to call a simple multiply function (e.g. a * b), works perfect. But when I turned to image data, there pops up an error called "The type initializer for 'Delegates' threw an exception."

The python script I used is:

import cv2
import numpy as np

def binarise(image):
    ret, img_output = cv2.threshold(image,100,255,cv2.THRESH_BINARY)
    return img_output

The c# I tried is:

 if (greyImage1 != null)
           {
               try
               {
                   var pythonPath = @"C:\Users\Admin\anaconda3";

                   Environment.SetEnvironmentVariable("PATH", $@"{pythonPath};" + Environment.GetEnvironmentVariable("PATH"));
                   Environment.SetEnvironmentVariable("PYTHONHOME", pythonPath);
                   Environment.SetEnvironmentVariable("PYTHONPATH ", $@"{pythonPath}\Lib");

                   string scriptFile = "myfunction.py";
                   string pythonCode = "";
                   using (var streamReader = new StreamReader(scriptFile, Encoding.UTF8))
                   {
                       pythonCode = streamReader.ReadToEnd();
                   }

                   using (Py.GIL())
                   {
                       var scope = Py.CreateScope();
                       scope.Exec(pythonCode);
                       greyImage1 = (scope as dynamic).binarise(greyImage1);
                       pictureBox1.Image = (System.Drawing.Image)greyImage1;
                       this.Cursor = Cursors.Default;
                   }
               }
               catch (Exception ex) { messageL.Text = ex.Message; }
           }

Anyone got any ideas? Appreciate your time and help.

5
  • Have a look here: stackoverflow.com/questions/10257000/… Commented Apr 29, 2021 at 15:41
  • what is greyImage1 here? When passing .net objects to pythonnet, you need to call ToPython() so you would need to do greyImage1.ToPython() but this still won't work since you need to change the image data type to what opencv expects , which is of type InputArray Commented May 27, 2021 at 17:50
  • Thank you. greyImage1 is in float, I've tried to change it to 2d array within python script, so that I can pass greyImage1 directly to function. But with no luck. What do you think is the reason for this? Commented May 28, 2021 at 8:21
  • Thank you roboto1986. I can change data type inside of python script to make it recognisable by opencv. I've also replaced greyImage1 to greyImage1.ToPython(). I believe data can be sent to python now, my problem now is after python function, how the data stored and returned to c#, to make sure it is recognisable by c#? Commented Jun 2, 2021 at 11:51
  • It seems after python, it returns as a pyobject, because I got an error [cannot implicitly convert type 'Python.Runtime.PyObject' to 'System.Drawing.Bitmap']. So how to go back to c# with right data type? Commented Jun 2, 2021 at 13:36

3 Answers 3

0

The main idea here is to call the script using a newly initialized process and get its standard output. This method needs an external Python interpreter to actually execute the script

public string run_cmd(string cmd, string args)
{
    ProcessStartInfo start = new ProcessStartInfo();
    start.FileName = "PATH_TO_PYTHON_EXE";
    start.Arguments = string.Format("\"{0}\" \"{1}\"", cmd, args);
    start.UseShellExecute = false;// Do not use OS shell
    start.CreateNoWindow = true; // We don't need new window
    start.RedirectStandardOutput = true;// Any output, generated by application will be redirected back
    start.RedirectStandardError = true; // Any error in standard output will be redirected back (for example exceptions)
    using (Process process = Process.Start(start))
    {
        using (StreamReader reader = process.StandardOutput)
        {
            string stderr = process.StandardError.ReadToEnd(); // Here are the exceptions from our Python script
            string result = reader.ReadToEnd(); // Here is the result of StdOut(for example: print "test")
            return result;
        }
    }
}

You can pass your command and argument which will get executed by given path of Python exe.

Remember that when you run such code you need to consider permissions of the account under which your main application is running. For example, if you want to run a script from the web service, that is hosted on IIS — be sure that all the script files that need to be executed have the right permissions for the user under which your web service’s ApplicationPool is running.

refer this LINK for more details.

Sign up to request clarification or add additional context in comments.

Comments

0

[Update] I'm pretty sure the problem occurs at this line: greyImage1 = (scope as dynamic).binarise(greyImage1); This acturally turns to data type conversion. Input and output data for python script are 2d array with uint8, but I'm not sure what data type c# expects or returns back.

[update] Data communication issue has been resolved. Result from c# is just numbers, when sent it to python, we need to make it a list, then make 2D array, and then convert to uint8, so that opencv can recognize it. Result from python can be returned directly back to c#. But remember it's PyObject, so need to change this to string, and then do whatever you want.

Comments

0

IronPython is perfect for this.

set a script up for usage:

using IronPython.Hosting;
ScriptEngine scriptEngine = Python.CreateEngine();
CompiledCode dealerAddress = scriptEngine.CreateScriptSourceFromFile("dealer.py").Compile();

Now you have dealerAddress ready to be executed.

Suppose dealer.py uses a variable "dealerName" in its script. Pass it from C# using ScriptScope.

ScriptScope scope = scriptEngine.CreateScope();
scope.SetVariable("dealerName", dealer.Name.ToUpper());

and finally execute it.

lock (dealerAddress) dealerAddress.Execute(scope);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.