【WPF】StyleTransferのPythonスクリプトをC#から実行する


はじめに

以下のPythonスクリプトを実行するためのGUIWPFで作成しました。
touch-sp.hatenablog.com

完成図

f:id:touch-sp:20211008211803p:plain:w600

方法

Python側で作成された多数の画像をどのようにC#側に渡すか?
いろいろ試しましたが結局いったんファイルに保存する最も簡単な方法を採用しました。

PYTHONNETを使う、メモリーを操作するなどなど方法はいろいろあるかと思いますがどれも難易度が高く今後の課題です。

Python側からC#にはファイル名だけ送信することにしました。それに従いC#でファイルを開きます。

開発環境

Windows 11 
Core i7-7700K + GTX 1080
Visual Studio Community 2019
.NET 5.0

Python 3.7.9
mxnet-cu102==1.7.0

Pythonスクリプト

変更点

今回のポイントはPython側の変更を最小限にできたことです。
元のPythonスクリプトに1行追加しただけです。

print(output_filename)

これでC#側にファイル名が送信されます。

最終的なPythonファイル

import sys
import os
import glob

import mxnet as mx

from master import net
from master import utils

os.makedirs('result', exist_ok=True)

if mx.context.num_gpus() >0:
    ctx = mx.gpu()
    content_size = 1024
    set_cuda = 1
else:
    ctx = mx.cpu()
    content_size = 512
    set_cuda = 0

input_file = sys.argv[1]
style_files = glob.glob('./master/images/styles/*.jpg')

content_image = utils.tensor_load_rgbimage(input_file, ctx, size=content_size, keep_asp=True)

style_model = net.Net(ngf=128)
style_model.load_parameters('./master/models/21styles.params', ctx=ctx)

for style_file in style_files:

    output_filename = os.path.basename(style_file)

    style_image = utils.tensor_load_rgbimage(style_file, ctx, size=512)
    style_image = utils.preprocess_batch(style_image)

    style_model.set_target(style_image)
    output = style_model(content_image)
    utils.tensor_save_bgrimage(output[0], os.path.join('result', output_filename), set_cuda)
    print(output_filename)

C#コード

MainWindow.xaml.cs

using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;

namespace StyleTransfer
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            WindowSettings();
        }
        private void PushExecuteButton(object sender, RoutedEventArgs e)
        {
            if (String.IsNullOrEmpty(selectedPythonPath)) return;
            if (String.IsNullOrEmpty(selectedImagePath)) return;

            Task<string> t = Task<string>.Run(() => PythonCall());
            string results = t.Result;

            string[] x = results.Split("\r\n");

            for(int i = 0; i < x.Length; i++)
            {
                if (!String.IsNullOrEmpty(x[i]))
                {
                    string currentPath = "result/" + x[i];
                    var bitmap = new BitmapImage();
                    using (var stream = File.OpenRead(currentPath))
                    {
                        bitmap.BeginInit();
                        bitmap.CacheOption = BitmapCacheOption.OnLoad;
                        bitmap.StreamSource = stream;
                        bitmap.EndInit();
                    }
                    imageBox[i].Source = bitmap;
                }
            }
        }
        private string PythonCall()
        {
            string result;

            var myProcess = new Process
            {
                StartInfo = new ProcessStartInfo(selectedPythonPath)
                {
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    CreateNoWindow = true,
                    Arguments = "transfer.py" + " " + selectedImagePath
                }
            };
            myProcess.Start();
            result = myProcess.StandardOutput.ReadToEnd();
            myProcess.WaitForExit();
            myProcess.Close();

            return result;
        }
    }
}

WindowSettings.cs

using Microsoft.Win32;
using System.Windows;
using System.Windows.Controls;

namespace StyleTransfer
{
    public partial class MainWindow : Window
    {
        string selectedPythonPath;
        string selectedImagePath;

        Button selectPythonButton = new Button()
        {
            Content = new Viewbox() { Child = new Label() { Content = "Select Python Interpreter" } },
            Margin = new Thickness(5)
        };
        Label selectedPythonLabel = new Label()
        {
            Content = "",
            VerticalAlignment = VerticalAlignment.Center,
            HorizontalAlignment = HorizontalAlignment.Left
        };

        Button selectImageButton = new Button()
        {
            Content = new Viewbox() { Child = new Label() { Content = "Select Image" } },
            Margin = new Thickness(5)
        };
        Label selectedImageLabel = new Label()
        {
            Content = "",
            VerticalAlignment = VerticalAlignment.Center,
            HorizontalAlignment = HorizontalAlignment.Left
        };

        Image[] imageBox = new Image[24];

        Button executeButton = new Button()
        {
            Content = new Viewbox() { Child = new Label() { Content = "Execute" } },
            Margin = new Thickness(5)
        };

        private void WindowSettings()
        {
            Title = "Style Transfer";

            selectPythonButton.Click += PushselectPythonButton;
            selectImageButton.Click += PushselectImageButton;

            executeButton.Click += PushExecuteButton;

            Grid mainGrid = Myfunc.NewGrid("1,1,10,1", "");
            Content = mainGrid;

            Grid pythonGrid = Myfunc.NewGrid("", "3,7");
            mainGrid.AddChild(pythonGrid, 0, 0, 1, 1);
            pythonGrid.AddChild(selectPythonButton, 0, 0, 1, 1);
            pythonGrid.AddChild(selectedPythonLabel, 0, 1, 1, 1);

            Grid ImageSelectGrid = Myfunc.NewGrid("", "3,7");
            mainGrid.AddChild(ImageSelectGrid, 1, 0, 1, 1);
            ImageSelectGrid.AddChild(selectImageButton, 0, 0, 1, 1);
            ImageSelectGrid.AddChild(selectedImageLabel, 0, 1, 1, 1);

            Grid ImageGrid = Myfunc.NewGrid("1,1,1,1", "1,1,1,1,1,1");
            mainGrid.AddChild(ImageGrid, 2, 0, 1, 1);

            for (int i = 0; i < 24; i++)
            {
                imageBox[i] = new Image() { Margin = new Thickness(5) };
                ImageGrid.AddChild(imageBox[i], i / 6, i % 6, 1, 1);
            }

            Grid ButtonGrid = Myfunc.NewGrid("", "1,1,1");
            mainGrid.AddChild(ButtonGrid, 3, 0, 1, 1);
            ButtonGrid.AddChild(executeButton, 0, 1, 1, 1);
        }
        private void PushselectPythonButton(object sender, RoutedEventArgs e)
        {
            var dialog = new OpenFileDialog();
            dialog.Filter = "Python実行ファイル (*.exe)|*.exe";
            if (dialog.ShowDialog() == true)
            {
                selectedPythonPath = dialog.FileName;
                selectedPythonLabel.Content = selectedPythonPath;
            }
        }
        private void PushselectImageButton(object sender, RoutedEventArgs e)
        {
            var dialog = new OpenFileDialog();
            dialog.Filter = "画像ファイル (*.jpg, *.png)|*.jpg;*png";
            if (dialog.ShowDialog() == true)
            {
                selectedImagePath = dialog.FileName;
                selectedImageLabel.Content = selectedImagePath;
            }
        }
    }
}

MyFunc.cs

using System.Windows;
using System.Windows.Controls;

static class Myfunc
{
    public static Grid NewGrid(string row, string col) //引数はカンマ区切りで
    {
        Grid grid = new Grid();

        string[] grid_row = row.Split(",");
        string[] grid_col = col.Split(",");

        if (grid_row.Length > 1)
        {
            foreach (string each in grid_row)
            {
                if (int.TryParse(each, out int each_num))
                {
                    grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(each_num, GridUnitType.Star) });
                }
                else
                {
                    grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
                }
            }
        }
        if (grid_col.Length > 1)
        {
            foreach (string each in grid_col)
            {
                if (int.TryParse(each, out int each_num))
                {
                    grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(each_num, GridUnitType.Star) });
                }
                else
                {
                    grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
                }
            }
        }
        return grid;
    }
    public static Grid AddChild(this Grid grid, UIElement element, int row, int col, int row_span, int col_span)
    {
        Grid.SetRow(element, row);
        Grid.SetRowSpan(element, row_span);
        Grid.SetColumn(element, col);
        Grid.SetColumnSpan(element, col_span);
        grid.Children.Add(element);

        return grid;
    }
}

さいごに

間違いや改善点があればコメント頂けましたら幸いです。