【C#】【Python】クリップボード経由でPythonからC#に画像を送る


はじめに

やりたいことはタイトル通りです。

「ProcessStart」を使った場合、通常Pythonから受け取れるデータはテキストデータ(コンソールに出力されるデータ)のみです。

今回はクリップボードを使って画像を受け取れるようにしました。

方法の概略

環境

Windows 10 
Visual Studio Community 2019 
.NET 5.0

Python 3.8.10

pythonはvenvで仮想環境を作りました。その環境に「pillow」と「pywin32」をpip経由でインストールしました。

pip install pillow
pip install pywin32

仮想環境のフルパスをC#のコードに書きますので仮想環境はどこに作っても問題ありません。

C#コードとPythonスクリプト

今回はサンプルとしてPythonのPILを使って画像を開くだけになっています。

Pythonスクリプトは「sample.py」という名前でexeファイルと同じフォルダに入れて下さい。

自分の場合にはこのようなフォルダになりました。
D:\dotnetformpython >> wpf >>wpf >> bin >> Debug >> net5.0-windows

画像は適当に用意して「sample.jpg」という名前でやはりexeファイルと同じフォルダに入れて下さい。

あとは実行するだけです。WPFのWindowに画像が表示されます。

Pythonスクリプト

import io
import sys
from PIL import Image
import win32clipboard as cb

image = Image.open(sys.argv[1])

with io.BytesIO() as f:
    image.convert('RGB').save(f, 'BMP')
    data = f.getvalue()[14:]

cb.OpenClipboard()
cb.EmptyClipboard()
cb.SetClipboardData(cb.CF_DIB, data)
cb.CloseClipboard()

C#コード

using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace wpf
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Image pictureBox = new() { Margin = new Thickness(3) };
            Content = pictureBox;

            string myPythonApp = "sample.py";
            string img_file = "sample.jpg";

            var myProcess = new Process
            {
                StartInfo = new ProcessStartInfo(@"D:\dotnetformpython\pythonenv\Scripts\python.exe")
                {
                    UseShellExecute = false,
                    RedirectStandardOutput = false,
                    CreateNoWindow = true,
                    Arguments = myPythonApp + " " + img_file
                }
            };

            using (myProcess)
            {
                myProcess.Start();
                myProcess.WaitForExit();
            }

            BitmapSource image = Clipboard.GetImage();
            if (image != null)
            {
                pictureBox.Source = new FormatConvertedBitmap(image, PixelFormats.Bgr32, null, 0);
            }
        }
    }
}

matplotlibの場合は?

先ほどはPILを使用しました。matplotlibの場合はどうするか?

numpy配列を経由してPILのImageに変換すれば動きました。ベストな方法かどうかはわかりません。

試しにPythonファイルを以下のように変えてみて下さい。また、「numpy」と「matplotlib」を追加でインストールして下さい。

import io
import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
import win32clipboard as cb

x = [1, 2, 3, 4, 5]
y = x

plt.plot(x, y)
fig = plt.gcf()

fig.canvas.draw()
im = np.array(fig.canvas.renderer.buffer_rgba())
im = Image.fromarray(im)

with io.BytesIO() as f:
    im.convert('RGB').save(f, 'BMP')
    data = f.getvalue()[14:]

cb.OpenClipboard()
cb.EmptyClipboard()
cb.SetClipboardData(cb.CF_DIB, data)
cb.CloseClipboard()

グラフが表示されます。

plt.plot(x, y)
fig = plt.gcf()

この部分は以下のようにも書けます。

fig, ax = plt.subplots()
ax.plot(x, y)

さいごに

今回は画像を開くだけの簡単なサンプルですが使い道は無限大だと感じています。

opencv-pythonを使って書いた画像処理スクリプトDeep Learningを使うために書いたスクリプトC#でそのまま有効利用できると思います。

一度画像をファイルに保存するという方法もあります。どちらかというとそちらの方が簡単ではありますがなんとなくそれはやりたくなかったというのが今回の動機です。

pythonnetを使うという方法もありますが導入までの敷居が高いです。私は挫折しました。

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

参考にさせて頂いたサイト