Pillowを使って画像を縮小させるスクリプトをメモしておこう!

はてなブログやホームページに画像をアップすると思ったより大きいと感じることがしばしばあったので、画像を縮小させるスクリプトを書いてみたのでメモしておく。
第一引数に対象ファイルを指定、第二引数に幅、高さを何分のいちにするかを指定。
対象のファイルはひとつだけ指定できる。

#
# shrink_image.py  file  div
#
import sys
import os
from PIL import Image

av = sys.argv
if len(av) < 3:
    r = '2'
else:
    r = av[2]

fn = sys.argv[1]
base,ext = os.path.splitext(os.path.basename(fn))
new_fn = base + "_" + r + ext 
print(new_fn)
img = Image.open(fn)
t = img.size
w,h = t
n = int(r)
tpl = (w//n,h//n)
print("{} => {}".format(t,tpl))
new_img = img.resize(tpl)
new_img.save(new_fn)

pythonスクリプトpowershellスクリプトをかぶせておこう。
対象のファイル・パスはパイプラインから受け取れるように、『PowerShell実践ガイドブック』のガイドに従い[CmdletBinding()]属性を付加してAdvanced Functionの形にした。これで対象のファイル・パスをパイプラインに流し込めばよい。

#
# shrink image  
#
Function Shrink-Image {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$True,
                   ValueFromPipeline=$True)]
        [ValidateNotNullOrEmpty()]
        [String]$Path,

        [int]$Div = 2
    )
    begin {
    }
    process {
        $cmd = "python '${home}\Documents\Python Scripts\shrink_image.py' "
        $cmd += "'"
        $cmd += $Path
        $cmd += "' "
        $cmd += $Div
        invoke-expression $cmd
    }
    end {
    }
}

実行例(パイプラインから渡す)

(base) PS >ls *.jpg  |  shrink-image -div 10
img0001_10.jpg
(800, 500) => (80, 50)
img0002_10.jpg
(800, 500) => (80, 50)
(base) PS >

ただ、メモしたこと自体を忘れてしまうかもしれない。

Windows8.1でモバイルホットスポットみたいなことをしたとき勝手にIPv4のプロパティが変更されていた現象に遭遇したときのメモ

最近までADSLルーターでネットに接続していたがADSLのサービスが打ち切られてからはスマホテザリングをする日々を過ごしてきた。が、デバイス間でのネットワークを構成できるルーターのようなものがあったらいいなと思い、モバイルホットスポットなるものがあることを知りちょっと試してみようと思った。
ルーター化したかったPCがWindows8.1だったので以下の記事を参照しながら試してみた。
あなたはもう使ってる?Windows8/8.1/10 無線LANルータ化の方法! - 生活情報オンライン
Windows8.1(ルーター役)にWindows10のPCをつなぐことはできたが、Windows10のPCからはインターネットアクセスはできなかった。Windows8.1(ルーター役)からはインターネットアクセスできていたが、あまり注意深く設定してなかったので、どこかで誤りがあったかもしれない。今から思うと恐らく「接続の許可」をしていなかったのではないかと思うが、しかし原因を追究する気力がなく、そのままシャットダウンしてしばらく使用していなかった。
普段は使用しないPCなので、しばらく放置していた。
久しぶりに起動してみると今度はこのPCがインターネットアクセスできなくなっている。
インターネットアクセスなし」と表示されている。
「なぜだろう?前までつながったのに?」
ネットを検索したりしたが、しばらく原因を突き止めることができないまま時間が過ぎる。
ふと、IPv4のプロパティ設定を見てみると、なんかおかしい?
「こんな設定していたっけな?やっぱりおかしい!」

ipv4property_error

いつのまにか固定IPアドレス指定、DNSサーバー固定になっていた。
これを元通り、「IPアドレスを自動的に取得」「DNSサーバーのアドレスを自動的に取得」に戻したらインターネットアクセスできるようになった。
これは、ただ設定を変更したことを失念してしまっていただけだったのかどうかは今はわからない。
心当たりのない現象だった。

ああ、このPCもあと5か月で現役引退か。

ExcelのSheetをPDFに変換するpowershellのスクリプトを試した

昔のスクリプトを取り出して試してみた。
対象のExcelファイルのシートをかたっぱしからPDFに変換するというシナリオで、PDFのファイル名は単純にファイル名に連番を付したものとした。
PDFへの変換にはWorkbook.ExportAsFixedFormat メソッド (Excel)を使用した。
Workbook.ExportAsFixedFormat メソッド (Excel) | Microsoft Docs

以下がコード。

#
#  export Excel sheet
#
$xlFixedFormat = "Microsoft.Office.Interop.Excel.xlFixedFormatType" -as [type] 
$excel = new-object -comobject excel.application
#$excel.visible = $True
$list = get-childitem . -include *.xlsx -recurse

$list | % {
    $_.FullName
    $excelbook = $excel.workbooks.open($_.FullName)
    for ( $i=0; $i -lt $excelbook.Sheets.Count; $i++ )
    {
        $n = $i + 1
        $excelbook.worksheets.item($n).Name
        $excelbook.worksheets.item($n).Select()
        $filename = $_.DirectoryName + "\" + $_.basename +'_{0:d2}' -f $n + ".pdf"
        $filename
        $excelbook.worksheets.item($n).ExportAsFixedFormat($xlFixedFormat::xlTypePDF, $filename) 
    }
}

$excel.quit()

ExportAsFixedFormat()をprintOut()にすると印刷できるはずである。
「はず」というのは確認できる環境はなく、確かではない。deprecatedとなっているかもしれない。もともとは印刷のためのスクリプトであったが、さすがにDXの時代にもう紙への印刷はないだろう・・・。

            $excelbook.worksheets.item($n).printOut()

ちなみにWindows10 HOME上でMicrosoft Office Home and Business2019のExcelを使って試した。

はてなブログにちょっと大きいGIFファイルを試しに入れてみる?

ちょっと大きめのGIFファイル(2.45MB)を「写真を投稿」と同じ要領でアップロードしてみた。
これは規約違反なのだろうか?

mandelbrot.gif

問題はファイルサイズだと思うけど今のところ今月の使用量は0%になっている。
まだまだいけるとちゃうか!

【愚痴】標準のpowershellではYAMLを扱えないことを知らなかった!XMLとJSONは扱えるのに・・・

PowershellXMLを扱える

[System.Xml.XmlDocument]型にすれば扱えるようだ。

#
# read XML file
#

function MyReadXML {
  [CmdletBinding()]
  param(
        [Parameter(Mandatory=$True,
                   ValueFromPipeline=$True)]
        [ValidateNotNullOrEmpty()]
        [string[]]$File
  )

  begin {
  }
  process {
    $doc = [System.Xml.XmlDocument](Get-Content -Encoding UTF8 -Raw $File )

    $x = $doc.root.StartPoint.X
    $y = $doc.root.StartPoint.Y
    $u = $doc.root.Pixel.Unit

    $property = @{
        X = $x
        Y = $y
        Unit = $u
    }
    $output = New-Object -TypeName PSObject -Property $property
    Write-Output $output
  }
  end {
  }
}

以下のようなXMLデータに対して

PS >cat .\p001.xml
<root>
  <StartPoint>
    <X>-0.17839816</X>
    <Y>0.652558</Y>
  </StartPoint>
  <Pixel>
    <Unit>7.15727999999993E-06</Unit>
    <Width>800</Width>
    <Height>500</Height>
    <Iteration>300</Iteration>
  </Pixel>
</root>

上記の関数で読み込ませると、Custom Objectで返してくれる(ようだ)。

PS >get-childitem *.xml | MyReadXML

Y               Unit                 X
-               ----                 -
0.652558        7.15727999999993E-06 -0.17839816
0.6500171656    1.71774720000006E-06 -0.17577143824
0.6498299311552 6.63050419200228E-07 -0.1755172116544

JSONも扱える

XMLのときとはデータの構造を変えてしまっているが、convertfrom-jsonでPSCustomObjectで返してくれる(ようだ)。

PS >cat .\ex1.json
{
    "startpoint":
      {
        "x":-0.17839816,
        "y":0.652558
      },
     "pixel":
      {
          "unit":7.15727999999993E-06,
          "width":800,
          "height":500
      },
      "iteration":300
}
PS >$co = ( get-content  .\ex1.json | convertfrom-json )
PS >$co

startpoint                   pixel                                               iteration
----------                   -----                                               ---------
@{x=-0.17839816; y=0.652558} @{unit=7.15727999999993E-06; width=800; height=500}       300


PS >$co.gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    PSCustomObject                           System.Object


PS >$co.startpoint.x
-0.17839816
PS >

YAMLは標準では扱えないみたいだ

qiita.com

ちょっと、標準でないモジュールを使うのは腰が引けるし、現在使用しているWindows PowerShellのバージョンは5.1でクロスプラットフォームpowershellに移行する度胸はなかった。以前、クロスプラットフォームのpwshをインストールして痛い目に会っているので、powershell自体をバージョンアップするのは私には危険すぎる。
やめておこう。

PS >$psversiontable

Name                           Value
----                           -----
PSVersion                      5.1.19041.1682
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.1682
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

水に浮く超小型電気自動車?FOMM ONE!

韓国の豪雨で道路が冠水したというニュースを聞き、ある日本のベンチャー企業の車が気になった。
www.asahi.com
First One Mile Mobility という近距離移動に最適なシステムを開発する会社のFOMM ONEという車で超小型ながら大人4人が乗車できる。
恐らく最も特徴的な点は水に浮くということだろう。
以下のホームページの中にプロモーションビデオ(YouTube)があり、その中で入水するシーンが見れる。
www.fomm.co.jp
ずいぶんと!(オッタマゲーションマーク)なクルマを作ったもんだ。
なお以下の記事によると超小型ではなく正しくは軽自動車に分類されるらしい。
超小型の電気自動車(EV)のラインナップを紹介! 価格やスペック、市販の有無も解説 - EV DAYS | EVのある暮らしを始めよう

powershellでBOMなしUTF8で出力しようとしてencodingに躓いたメモ

powershellでsqlite3に読み込ませるようにデータを加工していたときにEncodingに苦労したときのメモ。
どうもsqlite3は"BOMなしのUTF-8"でないとうまく読み込んでくれないようだったので、powershellからの出力結果を"BOMなしのUTF-8"にしようとしたが、なかなか情報を見つけられなかった。
手作業ならばメモ帳で開いて"BOMなしのUTF-8"で保存すればよいのだが、なるべくコードに落とし込みたかったので。
Windows10でpowershellのバージョンは以下の通り。

PS >$psversiontable

Name                           Value
----                           -----
PSVersion                      5.1.19041.1682
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.1682
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

encodingをUTF8にして、「Set-Content -Encoding UTF8」で保存するとBOMがついてきてしまう。UTF8NoBOMはサポートされていないようだし、C#のコードを書くという解もあるようだったが腰が引けてしまう。
諦めかけていたときに以下のページを見つけた。
maywork.net
助かりますね。

function Out-FileNoBOM
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$True, ValueFromPipeline=$True)]
        [string[]]$Lines,
        [Parameter(Mandatory=$True, ValueFromPipeline=$False)]
        [string]$FilePath
    )
    begin
    {
        $fs = New-Object System.IO.StreamWriter($FilePath, $false)
    }
    process
    {
        foreach($line in $Lines)
        {
            $fs.WriteLine($line)
        }
    }
    end
    {
        $fs.Close()
    }
}

$f = join-path  $pwd  data.tsv
$f
.\aaa.ps1 | Out-FileNoBOM -filepath $f

sqlite3でファイル"data.tsv"をテーブル"book"にインポート。".mode tabs"でタブ区切りに指定。

PS >sqlite3 .\test.sqlite
SQLite version 3.30.1 2019-10-10 20:19:45
Enter ".help" for usage hints.
sqlite> .mode tabs
sqlite> .import data.tsv  book

PowerShell (v6 以上) は、すべてのテキスト出力に utf8NoBOM 対して既定で に設定されます。」というから将来はデフォルトになるのだろう。
お恥ずかしながら、私は-Join '`t'とシングルクォートを使ってうまくいかないことにも躓いた。最初はシングルクォートで括っていてしまい意図した通りに動かないことの原因に気付かなかった。たぶんシングルクォートで括ってしまうと特殊な意味を失いエスケープシーケンスでなくなってしまうのね。変数を展開しないだけではないのね。

....
% { ($_.ToString().Split(',')[1,3]) -Join "`t" }
...

どうも私は体で覚えないといけないタイプのようだ。

【備考】
その後、日付のフォーマットを変更したり、両端のダブルクォーテーションをトリミングしたりと手を加えた。
まずは primary keyが重複しないように現在の最大値を取得し、'max.txt'に出力するSQLファイルを用意。

PS >cat .\sql.txt
.output ./max.txt
select max(id) from book;

.readでSQLファイルを読み込ませて実行させる。

$tmp = 'tmp.txt'
sqlite3  .\publib.sqlite ".read sql.txt"
$mf = 'max.txt'
$s = get-content($mf)
if ( [string]::IsNullOrEmpty($s) ) {
    $cnt = 1
}
else {
    $cnt = [int]::parse($s) + 1
}

$d = 'D:\Document\PublicLibrary'

get-childitem $d -filter RENT*.csv | sort-object |`
% { get-content $_.fullname } | select-string '貸出資料一覧' -notmatch |`
 select-string 'タイトル' -notmatch |`
 select-string '貸出状況照会' -notmatch |`
 select-string '^$' -notmatch |`
% { $a =($_.ToString().Split(',')[1,3]); $a[0].trim('"') + "`t" + ([DateTime]($a[1] -replace('"',''))).ToString('yyyy-MM-dd') } |`
sort-object -unique  > $tmp

$f = ( get-content $tmp )

for ( $i=0; $i -lt $f.length; $i++ ){
    $cnt.tostring() + "`t" + $f[$i]
    $cnt++
}

sqlite3はimportするとき両端の"をトリミングしてくれているようなので気づいていなかったが、MySQLにそのままimportすると両端のダブルクォーテーションが残ってしまうのであった。encodingだけでなく結構、データを加工しないといけないとは・・・。