読者です 読者をやめる 読者になる 読者になる

Itsukaraの日記

最新IT技術を勉強・実践中。最近はDeep Learningに注力。

PowerShellでGUI: 可変個数RadioButton実現方法(別法)

VisualStudioでPowerShell用GUIを作成する方法 - Itsukaraの日記」という記事で、PowerShellGUI作成用にVisualStudioを使う方法を紹介し、可変個数のRadioButtonを表示する方法も書きました。しかし、可変個数のRadioButtonは、もっと良い実現方法を考えたので、紹介します。

とても簡単で、GUIの構造を表すXAML文字列を書き換えるだけです。下記にプログラム例を示します。下記の「add_items_to_xaml」という関数が、XAMLを書き換える処理です。汎用性があるので、RadioButton以外でも使えると思います。

  • 可変個数のRadioButtonを作る例:
$ErrorActionPreference = "stop"
Set-PSDebug -Strict
Add-Type -AssemblyName PresentationFramework

$xaml = @'
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="xaml test #2"
    Width="170pt" Height="170pt">
  <StackPanel>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
      <StackPanel x:Name="country">
      </StackPanel>
      <Label Content="  " FontSize="10pt"/>
      <StackPanel x:Name="fruit">
      </StackPanel>
    </StackPanel>
    <Label x:Name="msg"  Content="  " FontSize="10pt"/>
    <Button x:Name="btn" Content="Push Me"  FontSize="10pt" />
  </StackPanel>
</Window>
'@

# 項目一覧を$xamlに追加
# $head : 置換前文字列
# $add  : 置換後文字列のひな型
# $placeholder: $add中のプレスホルダー文字列
# $items: 項目一覧(空白で区切られたワード列)
function add_items_to_xaml($head, $add, $placeholder, $items) {
    $list = $items.split(" ")
    [array]::Reverse($list)
    foreach ($item in $list) {
        $add_item = $add.Replace($placeholder, $item)
        $global:xaml = $global:xaml.Replace($head, $add_item)
    }
}

# countryへの項目追加
$country_head = @'
      <StackPanel x:Name="country">
'@
$country_add = @'
      <StackPanel x:Name="country">
        <RadioButton Content="PlaceHolder"></RadioButton>
'@
$countries = "U.S.A China Japan Germany France England Russia"
add_items_to_xaml $country_head $country_add "PlaceHolder" $countries

# fruitへの項目追加
$fruit_head = @'
      <StackPanel x:Name="fruit">
'@
$fruit_add = @'
      <StackPanel x:Name="fruit">
        <RadioButton Content="PlaceHolder"></RadioButton>
'@
$fruits = "Apple Orange Grape"
add_items_to_xaml $fruit_head $fruit_add "PlaceHolder" $fruits

$xaml

$xaml = [xml]$xaml
$reader = New-Object System.Xml.XmlNodeReader $xaml
$frm = [System.Windows.Markup.XamlReader]::Load($reader)
$country = $frm.FindName("country")
$fruit = $frm.FindName("fruit")
$msg = $frm.FindName("msg")

# 選択された項目を返す
function getChecked($parent) {
    $checked = ""
    foreach ($child in $parent.Children) {
        if ($child.isChecked) {
            $checked += $child.Content
        }
    }
    return $checked
}

# ボタンクリック時の処理
function clicked {
    $selectedCountry = getChecked $country
    $selectedfruit  = getChecked $fruit
    $msg.content = "Country=" + $selectedCountry + "`n" + "Fruit=" +  $selectedfruit
}

$btn = $frm.FindName("btn")
$btn.Add_Click({clicked})
$result = $frm.ShowDialog()
  • 書換後のXAML(上記プログラムで出力したもの)
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="xaml test #2"
    Width="170pt" Height="170pt">
  <StackPanel>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
      <StackPanel x:Name="country">
        <RadioButton Content="U.S.A"></RadioButton>
        <RadioButton Content="China"></RadioButton>
        <RadioButton Content="Japan"></RadioButton>
        <RadioButton Content="Germany"></RadioButton>
        <RadioButton Content="France"></RadioButton>
        <RadioButton Content="England"></RadioButton>
        <RadioButton Content="Russia"></RadioButton>
      </StackPanel>
      <Label Content="  " FontSize="10pt"/>
      <StackPanel x:Name="fruit">
        <RadioButton Content="Apple"></RadioButton>
        <RadioButton Content="Orange"></RadioButton>
        <RadioButton Content="Grape"></RadioButton>
      </StackPanel>
    </StackPanel>
    <Label x:Name="msg"  Content="  " FontSize="10pt"/>
    <Button x:Name="btn" Content="Push Me"  FontSize="10pt" />
  </StackPanel>
</Window>
  • 画面表示例:

f:id:Itsukara:20170324210102p:plain

あとがき

今回の件に関して、Webで色々と見つけたのですが、よい方法が見つからず、結局、自分で考えました。でも、後から考えると当たり前の内容であり、Webで色々調べた労力が無駄でした。

VisualStudioでPowerShell用GUIを作成する方法

PowerShellGUIを使う記事を下記2件書きましたが、PowerShellのプログラムを動かすまでGUIを確認できず不便と思っていました。もしかしたら、VisualStudioを使うと、画面設計ができなかと思ったら、簡単に出来たので報告します。

VisualStudioを使ったGUI設計

  • VisualStudioを立ち上げる。
  • ファイル⇒新規作成⇒プロジェクトを選択。

f:id:Itsukara:20170322232630p:plain

  • WPFアプリケーションを選択。

f:id:Itsukara:20170322232635p:plain

  • 下記ようなGUI設計画面になるので、左のツールボックスから部品を選んで、中央のウィンドウにドラッグして配置しGUIを設計*1

f:id:Itsukara:20170322232640p:plain

自動生成されたXAMLを使いPowerShellプログラム作成

  • 例えば下記のようにGUI部品を配置。

f:id:Itsukara:20170322232649p:plain

  • 画面下部のXAML(文字列)を抜き出してPowerShellのプログラムを作成。
  • ただし、一部の文字列は削除が必要(下記の黄色枠部分)。

f:id:Itsukara:20170322235005p:plain

  • 下記がプログラムの例。「@'」と「'@」で囲まれた部分がXAML
$ErrorActionPreference = "stop"
Set-PSDebug -Strict
Add-Type -AssemblyName PresentationFramework

[xml]$xaml = @'
<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Label x:Name="label01" Content="名前" HorizontalAlignment="Left" Margin="15,26,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="textName" HorizontalAlignment="Left" Height="26" Margin="68,26,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="439"/>
        <Label x:Name="label02" Content="ふりがな" HorizontalAlignment="Left" Margin="15,57,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="textFurigana" HorizontalAlignment="Left" Height="26" Margin="68,57,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="439"/>
        <Label x:Name="label03" Content="性別" HorizontalAlignment="Left" Margin="15,88,0,0" VerticalAlignment="Top"/>
        <StackPanel x:Name="stackGender" HorizontalAlignment="Left" Height="26" Margin="68,88,0,0" VerticalAlignment="Top" Width="129" Orientation="Horizontal">
            <RadioButton x:Name="radioButtonMale" Content="Male" Height="26" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5,5,5,0"/>
            <RadioButton x:Name="radioButtonFemale" Content="Female" Height="26" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5,5,5,0"/>
        </StackPanel>
        <Label x:Name="label04" Content="国籍" HorizontalAlignment="Left" Margin="15,119,0,0" VerticalAlignment="Top"/>
        <Label x:Name="label05" Content="年齢" HorizontalAlignment="Left" Margin="15,150,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="textAge" HorizontalAlignment="Left" Height="26" Margin="68,150,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="52"/>
        <ComboBox x:Name="comboBoxCountry" HorizontalAlignment="Left" Height="26" Margin="68,119,0,0" VerticalAlignment="Top" Width="439"/>
        <Button x:Name="buttonRegist" Content="登録" HorizontalAlignment="Left" Height="33" Margin="242,276,0,0" VerticalAlignment="Top" Width="48"/>
        <TextBlock x:Name="textBlock" HorizontalAlignment="Left" Height="90" Margin="15,181,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="492"/>

    </Grid>
</Window>
'@

$reader = New-Object System.Xml.XmlNodeReader $xaml
$frm = [System.Windows.Markup.XamlReader]::Load($reader)

$countries = "U.S.A China Japan Germany France England Russia".split(" ")
$comboBoxCountry = $frm.FindName("comboBoxCountry")
foreach ($country in $countries) {
  [void]$comboBoxCountry.Items.Add($country)
}

$textName = $frm.FindName("textName")
$comboBoxCountry = $frm.FindName("comboBoxCountry")
$textFurigana = $frm.FindName("textFurigana")
$stackGender = $frm.FindName("stackGender")
$textAge = $frm.FindName("textAge")
$textBlock = $frm.FindName("textBlock")

function getChecked($parent) {
    $checked = ""
    foreach ($child in $parent.Children) {
        if ($child.isChecked) {
            $checked += $child.Content
        }
    }
    return $checked
}

function regist {
    $name = $textName.Text
    $furigata = $textFurigana.Text
    $gender = getChecked $stackGender
    $country = $comboBoxCountry.SelectedValue
    $age = $textAge.Text

    $msg = ""
    $msg += "名前:" + $name + "`n" 
    $msg += "ふりがな:" + $furigata + "`n" 
    $msg += "性別:" + $gender + "`n" 
    $msg += "国籍:" + $country + "`n" 
    $msg += "年齢:" + $age + "`n" 

    $textBlock.Text = $msg
}

$buttonRegist =  $frm.FindName("buttonRegist")
$buttonRegist.Add_Click({regist})

$result = $frm.ShowDialog()
  • プログラム実行例。名前等を入力後、「登録」ボタンを押したところ。

f:id:Itsukara:20170322232657p:plain

あとがき

VisualStudioを使うと、PowerShellGUIがとても簡単に設計できるので、ぜひ試してみてください。

おまけ

ListBoxやComboBoxは、PowerShellから簡単に項目を追加できるが、RadioButtonは簡単に追加できない。これはとても不便だが、次のようにプレースホルダを沢山書いておき、必要なものだけ表示するようにPowerShellで制御すれば、ある程度対応できることが分かった。ご参考まで。

  • プログラム例:
$ErrorActionPreference = "stop"
Set-PSDebug -Strict
Add-Type -AssemblyName PresentationFramework

[xml]$xaml = @'
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="xaml test #2"
    Width="170pt" Height="170pt">
  <StackPanel>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
      <StackPanel x:Name="country">
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
      </StackPanel>
      <Label Content="  " FontSize="10pt"/>
      <StackPanel x:Name="fruits">
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
        <RadioButton Content="PlaceHolder"></RadioButton>
      </StackPanel>
    </StackPanel>
    <Label x:Name="msg"  Content="  " FontSize="10pt"/>
    <Button x:Name="btn" Content="Push Me"  FontSize="10pt" />
  </StackPanel>
</Window>
'@

$reader = New-Object System.Xml.XmlNodeReader $xaml
$frm = [System.Windows.Markup.XamlReader]::Load($reader)
$country = $frm.FindName("country")
$fruits = $frm.FindName("fruits")
$msg = $frm.FindName("msg")

# 全てのRadioButtonを非表示にする
foreach ($s in $country.Children) {
    $s.Visibility = "Collapsed"
}

# 全てのRadioButtonを非表示にする
foreach ($b in $fruits.Children) {
    $b.Visibility = "Collapsed"
}

# RadioButtonのContentを設定し、表示する
$countryList = "U.S.A China Japan Germany France England Russia".split(" ")
$n = 0
$children = $country.Children
foreach ($c in $countryList) {
    $radioButton = $children[$n]
    $radioButton.Content = $c
    $radioButton.Visibility = "Visible"
    $n += 1
}

# RadioButtonのContentを設定し、表示する
$fruitsList = "Apple Orange Grape".split(" ")
$n = 0
$children = $fruits.Children
foreach ($f in $fruitsList) {
    $radioButton = $children[$n]
    $radioButton.Content = $f
    $radioButton.Visibility = "Visible"
    $n += 1
}

# 選択された項目を返す
function getChecked($parent) {
    $checked = ""
    foreach ($child in $parent.Children) {
        if ($child.isChecked) {
            $checked += $child.Content
        }
    }
    return $checked
}

function clicked {
    $selectedCountry = getChecked $country
    $selectedFruits  = getChecked $fruits
    $msg.content = "Country=" + $selectedCountry + "`n" + "Fruits=" +  $selectedFruits
}

$btn = $frm.FindName("btn")
$btn.Add_Click({clicked})
$result = $frm.ShowDialog()
  • 実行例:

f:id:Itsukara:20170323002422p:plain

*1:それぞれの部品の意味は、別のサイト等で調べてください。

PowerShell+WPFが動かない場合の原因と対策

仕事の関係でPowerShellを使ったので、PowerShellでの勉強内容と作成プログラムを記事に書きましたが、本番で動かず、困ってしまいました。同様の問題で困っている方がいるかもしれないので、解決先を書きます。

問題点

原因と解決方法

まとめ

PowerShellは、スクリプトがネットワークドライブにあるか、どのバージョンか、PowerShellPowerShell ISEか、によって動作が異なるので注意が必要。]

  • 対策:「powershell -sta -ExecutionPolicy Bypass ファイル名」で起動。
  • 別案:「powershell -sta -file ファイル名」で起動(2/18追記)。

PaintsChainerが爆速になっていた

http://itsukara.hateblo.jp/entry/2017/01/29/064926:embed:先週の記事で書いた線画着色(PaintsChanier)の件、久しぶりに最新版をダウンロードして試してみたら、当方の貧弱なGPU RAM(2GB)でも動くようになっており、サイズが500x500位の画像では3秒ぐらいで処理が完了。爆速になっていた。

線画着色最新版が妙に青みがかっている

線画着色の件、最新版(2017/1/31 21:39; 03739fd)を本家github(下記)からダウンロードして試したところ、残念ながら、特に速くなったり、機能が追加されている気はしません。また、残念ながら、色が妙に青みがかっています。

github.com

1/29ダウンロード版で生成した画像

f:id:Itsukara:20170131215053p:plain

1/31 21:39ダウンロード版で生成した画像

f:id:Itsukara:20170131214721p:plain

githubサイトを見ると、OpenCVのインストール方法として、結構面倒な方法へのリンク(下記)が書かれているので、それをやらないとだめなのかと思ったのですが、下記サイトに書かれたテスト用のデモを現在環境で実行したところ、下記サイトに表示される絵が、そのままの色で表示され、何の問題も発生しませんでした。ということは、下記の面倒はインストールをしなくても、"conda install opencv"で十分だったということです。Preferred Networkさん、どうなっているでしょう... (線画着色の件、githubにcommitしている人を見ると、Preferred Networkの人が多数関わっています。Preferred Networkが開発したChainerの宣伝にもなるので、Taizanだけでなく、Preferred Network総がかりで取り組んでいるのだと思います)

  • チェック用プログラムを走らせ、上記サイトのウィンドウに重ねて表示したところ。

f:id:Itsukara:20170131215600j:plain

上記表示結果を基に、本家サイトのIssueで質問予定です。

ちなみに、以前のソースと比べたら、img2imgDataset.pyの旧版と新版で下記のように変わってますが、学習データは旧版のソースコードで学習させていると思いますので、「現在のソースコード」と「旧ソースコードで作成した学習データ」の間での不整合が原因な気がします。
旧版:image2 = cv2.cvtColor(image2, cv2.COLOR_BGR2YUV)
新版:image2 = cv2.cvtColor(image2, cv2.COLOR_RGB2YUV)

本件、githubのissueで議論中です。下記を参照ください。学習データはYUVなので、単に、画像の入出力がRGBかBGRかという問題とのこと。これは、@abbychauさんが解決しました(OpenCVのバージョンを見て自動的に判定できるらしいです)
github.com

線画着色で満員御礼!

当サイトはこれまで平均100PV/日程度のアクセス数だったのですが、昨日と本日は、各1日で1か月分のPVを頂きました。関心を持って頂き、まことにありがとうございます。とても励みになります。
f:id:Itsukara:20170131005452p:plain

ついでに、他の記事も見てくれると更にうれしいです。例えば、OpenAI GymのMontezuma's Revengeで1位になると共に、Deep Mindの論文に書かれていない部屋にも到達した話とか... これがきっかけでOpenAIの本社に招待され、12月にサンフランシスコに行ってきました(費用はOpenAI負担)。
itsukara.hateblo.jp

DeepMindの論文に書かれていない部屋に到達した件は、論文の著者Marc G. Bellemare氏にもお知らせ済で、素直に喜んでくれています(下記youtubeのKorekara Itsukaraのコメントと、Marc G. Bellemare氏のコメント参照)。
Playing Montezuma's Revenge with Intrinsic Motivation - YouTube

OpenAI Gymのスコアは下記参照。1位~3位が当方です。
gym.openai.com

写真から輪郭を抽出し線画着色

ローカルに線画着色できるので、色々試してみました。

今回、写真から輪郭を抽出し、それを基に線画着色してみました。輪郭を抽出するjupyter notebookはgithubにアップしましたので、パラメーターをいろいろ変えて試してみてください。以下では、googleで「美人」で検索した画像から輪郭を抽出し、着色したものです。最後の写真以外、ヒントは無しです。

美人#1

元写真ほど良くないですが、結構きれいな画像になりました。
f:id:Itsukara:20170129223034p:plain

美人#2

元写真は知的で若い女性の写真ですが、おばさんになってしまいました。
f:id:Itsukara:20170129223218p:plain

美人#3

元写真は野性的で若い美人ですが、抽象画になってしまいました。
f:id:Itsukara:20170129223842p:plain

美人#4

元写真と比べ良い線画が撮れませんでしたが、着色したら、それなりになりました。
f:id:Itsukara:20170129224246p:plain

美人#5

細かい線画が撮れ、着色したら、まあまあの出来になりました。
f:id:Itsukara:20170129224540p:plain

建物#1

建物は、輪郭がはっきりしているので、よい線画ができました。ただ、着色したら輪郭があいまいになってしまいました。
f:id:Itsukara:20170129224859p:plain

元の写真は下記です。
http://chihiro015.up.n.seesaa.net/chihiro015/image/IMG_6731.JPG?d=a1

風景#1

森を抜ける階段のようですが、色使いが今一つです。
f:id:Itsukara:20170129225512p:plain

4点ほど緑を加え、会談に灰色を2か所入れました。緑にはなりましたが、下記写真と比べると、今一つですね。
f:id:Itsukara:20170129225725p:plain

元の写真は下記です。
http://travel.tycg.gov.tw/Utility/DisplayImage?id=11271

追記

このように、Webから写真を取ってきて、そこから線画を抽出し、それを基に着色し、着色画像を写真と比較する処理を、自動化して、何百万枚も学習させると、更に凄いものになりそうな気がします。ついでに、線画抽出の部分もDeep Learningで作ると、線画抽出と、線画着色と、一緒に学習できますね。大学や研究機関で豊富なマシンパワーがある方は、ぜひ試してみてください。