月別アーカイブ: 2023年1月

deprecated: SafeConfigParser

Python 3.11 は SafeConfigParser が非推奨になっていた。

deprecated

3.12 で完全削除のようです、3.11 は一応まだ動く。
今のうちに 2013 年に作った下記ページを書き換えとかないと。

INI ファイルの読み書き – Paepoi

てか GTK3 のコードだし、require_version していないし。
それと文字列をシングルクォートに統一でココは手をつけていないし。
自作クラスのほうも今なら正規表現にするし。

configparser — 設定ファイルのパーサー ? Python 3.11.1 ドキュメント

configparser.ConfigParser にすればいいのかな?
GTK4 で書き換えしてみよう。

#! /usr/bin/env python3

import os, configparser, gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk

INI = 'test.conf'

class Win(Gtk.ApplicationWindow):
    '''
        ini(conf) の読み書き例
        Window の位置と大きさを保存と復元
    '''
    def __init__(self, a):
        '''
            起動時に ini を読み込む
            GTK4 は位置指定できないのかな?
        '''
        Gtk.ApplicationWindow.__init__(self, application=a, title='ini')
        # 設定ファイルを探す
        if os.path.exists(INI):
            # configparser を作成し読み込む
            conf = configparser.ConfigParser()
            conf.read(INI)
            # 後での追記を考えて has_opthon しておこう
            cx = 200
            cy = 200
            if conf.has_option('window', 'width'):
                cx = conf.getint('window', 'width')
            if conf.has_option('window', 'height'):
                cy = conf.getint('window', 'height')
            self.set_default_size(cx, cy)

    def do_close_request(self):
        '''
            GTK3 では do_delete_event
            終了時に ini に書き込み
        '''
        conf = configparser.ConfigParser()
        if os.path.exists(INI):
            conf.read(INI)
        # [window] セクションが存在しなければ追加
        if not 'window' in conf.sections():
            conf.add_section('window')
        # サイズを取得(タプルで戻る)して conf にセット
        cx, cy = self.get_default_size()
        conf.set('window', 'width', str(cx))
        conf.set('window', 'height', str(cy))
        # ファイルに書き込む
        with open(INI, 'w') as f:
            conf.write(f)
        return False

app = Gtk.Application()
app.connect('activate', lambda a: Win(a).present())
app.run()

これでイケるようだ。
そういえば GTK4 でウインドウの位置指定ってまだ調べていなかったな。

Composition

先週は WebP ネタが長くなって写真が貼れなかった。
今日は用事があったので撮影無し。
ということで先週分のを今日になって RAW 現像。

01

ジョビ子を地上にて日の丸構図、しゃがんでなるべく水平に。
今更だけどパナライカ 100-400 で地上の前ボケはイマイチ。

02

この子は大サービスで拙者もさせてくれました。
こういう枝並の前ボケならいい感じなんですけど。

03

モズ男、動物写真は目線の方向にスペースを開ける。
トリミングでそうするより最初から狙ったほうが自然になるね。

04

ヨシガモ男、あえて逆にしてみたらコッチ見てる感になった。
トリミングして被写体を大きく見せるより構図を工夫したほうが面白い。

で、遅ればせながら macOS Ventura 13.2 にアップデート。
LUMIX G99 を USB 接続、おぉ普通に写真を表示できるように戻った。
なんか Monterey の頃より遅い気がするけどまあいいや。

久々に G99 を触って今頃気がついたんですけど。
追尾 AF ターゲットも EVF を覗きながらタッチで動かせるのね。
今度からコレ使おう、まだ知らない機能あるかも。

Change 72dpi Screenshot

前回画像を PIL で WebP 化するネタを書いたけど。
どうせならスクリーンショットの 72dpi 化も同時にやりたい。
72dpi にして WebP にして、って二度手間じゃん。

色々試すとそのまま Image.save は全部 72dpi になるらしい。
ということは半分にリサイズしてセーブするだけですね。

import sys, os, re
from PIL import Image

arg = sys.argv[1:]
for s in arg:
    if re.search(r'\.(jpe?g|png|gif)$', s, re.I):
        im = Image.open(s)
        # 144dip to 72dpi
        x, y = im.size
        im_s = im.resize((x//2, y//2), Image.Resampling.LANCZOS)
        im_s.save(f'{os.path.splitext(s)[0]}.webp')

検索でよく見つかる Image.ANTIALIAS は非推奨になっているので注意。
これを前回と同様に Automator でメニューに追加。

mm

ところで前回 .zprofile をフルパスでって書いたけど。
チルダでイケた、ユーザーは自分自身だもんね。

ss

Pillow って思っていたより高機能なんですね、もっと色々やってみよう。
Pillow (PIL Fork) 9.4.0 documentation

WebP conversion @ Automator

[CGImageDestinationCreateWithURL webp] で検索。
以下は一年前に書かれたものだけど。

ios – “unsupported file format ‘org.webmproject.webp'” while saving CGImage in webp format – Stack Overflow

#!/usr/bin/env python3

from Quartz import *

src_type = CGImageSourceCopyTypeIdentifiers()
CFShow(src_type)
dest_type = CGImageDestinationCopyTypeIdentifiers()
CFShow(dest_type)

macOS 13 Ventura でも非サポートなんですね。
どうやら OS 頼りは現状無理っぽい。

しかたがない、Python や PHP を使う。
WebP を使うような人ならどちらかは入れているはず。
これでコマンドを作って Automator で呼び出しする方法を。

PHP: imagecreatefromjpeg – Manual

ココでは Python3 を使う、PHP でやる人は上記サンプルコードを参考に。
Python3 では Pillow(PIL) が必要、残念ながら HEIF は未対応。

pip install Pillow

いつのまにか pip3 でなく pip でイケるようになってる。
python3 も 3 を打たなくていいようにしてよ、Fedora みたく。
そしてコマンドを書く。

#!/usr/bin/env python3

import sys, os, re
from PIL import Image

arg = sys.argv[1:]
for s in arg:
    if re.search(r'\.(jpe?g|png|gif)$', s, re.I):
        im = Image.open(s).convert('RGB')
        im.save(f'{os.path.splitext(s)[0]}.webp', 'webp')

を拡張子無し実行パーミッション有りでパスの通った場所に置く。
名前は img2webp とか解りやすい名前にしておく。

Automator で新規書類、クイックアクションを選択。
「指定されたFinder項目を取得」のアクションをドラッグで配置。
「シェルスクリプトを実行」を同様に配置。
ワークフローが受け取る現在の項目を「イメージファイル」に。
入力の引渡し方法を「引数として」に。
そしてシェルスクリプトに自作コマンドを呼び出すコードを。

# Automator は読み込まないのでフルパスをドットコマンド
# もちろん source コマンドでもいい
. /Users/sasakima-nao/.zprofile
. /Users/sasakima-nao/.zshrc
# 自分が付けたコマンド名にしてね
img2webp "$@"

automator

いや、~/.zshrc とかに何も登録していないなら読み込まなくていいけど。
Python 3.10 は ~/.zprofile に書き込みしているから呼び出せるのであって。
コレをしないと場合によっては古いバージョンを呼び出しとかしてしまう。
読み込まない又はパスを通していない場合は自作コマンドもフルパスにしてね。
とにかくコレを create_webp とか解りやすい名前で保存。

menu

困ったことにデジカメの RAW 画像でも出ちゃうんだなこれが。
なので拡張子で振り分けする処理は必須です、おしまい。
このスクリーンショットはコレで変換した WebP 画像でした。

Image conversion @ PyObjC and JXA

前回書いた検索で簡単に見つかる画像変換ですが。
Objective-c での公式は以下に。

Viewing, Editing, and Saving Images in an Image View

PyObjC でしたら以下がありますね。

[PyObjC CoreGraphics API Example] Shows how to use the functional API of the CoreGraphics framework through PyObjC and Python 2.7. #pyobjc #python #coregraphics #quartz #macos #examples ? GitHub

ただし Python2 コードですし kUTTypeJPEG はとうの昔に非推奨です。
Python3 にて UniformTypeIdentifiers.UTTypeJPEG を使うよう書き換え。

System-declared uniform type identifiers | Apple Developer Documentation

したんだけど何度やってもエラー、原因が解らず困っていた。
本日原因が判明した、こんな理由だったとは。

#!/usr/bin/env python3

import UniformTypeIdentifiers, LaunchServices

print(type(LaunchServices.kUTTypeJPEG))
print(type(UniformTypeIdentifiers.UTTypeJPEG))

''' output
<class 'objc.pyobjc_unicode'>
<objective-c class _UTCoreType at 0x1e3f02bd0>

'''

まさかの。
PyObjC の kUTTypeJPEG は単なる文字列だった。

UTTypeJPEG は普通にポインタ、そりゃエラーになるよと。
ということで PyObjC ではこんなコードになります。
破棄は Python にまかせないと二重破棄でエラー。

#!/usr/bin/env python3

from AppKit import *
from Quartz import *
from UniformTypeIdentifiers import *

src = NSURL.fileURLWithPath_('panaleica.heic')
dest = NSURL.fileURLWithPath_('panaleica.jpg')

isr = CGImageSourceCreateWithURL(src, None)
image = CGImageSourceCreateImageAtIndex(isr, 0, None)

destimg = CGImageDestinationCreateWithURL(dest, str(UTTypeJPEG), 1, None)
if destimg:
    CGImageDestinationAddImage(destimg, image, None)
    res = CGImageDestinationFinalize(destimg)
    print(res)
    #CFRelease(destimg) # Error
else:
    print('error')

HEIF や WebP からでも JPEG に変換できる。
ただし iPhone 写真等の回転情報は継承されず横向きになるので注意。

rotate

いやオートメーションで使うなら AppleScript か JXA にしないと。
こちらでは UniformTypeIdentifiers が使えないので直接文字列で。

#!/usr/bin/osascript

ObjC.import('Cocoa');
ObjC.import('Quartz');
//ObjC.import('UniformTypeIdentifiers');

let src = $.NSURL.fileURLWithPath('gara.webp');
let dest = $.NSURL.fileURLWithPath('gara.heic');

let isr = $.CGImageSourceCreateWithURL(src, null);
let image = $.CGImageSourceCreateImageAtIndex(isr, 0, null);

//let destimg = $.CGImageDestinationCreateWithURL(dest, $('public.jpeg'), 1, null); // jpeg
//let destimg = $.CGImageDestinationCreateWithURL(dest, $('public.png'), 1, null); // png
let destimg = $.CGImageDestinationCreateWithURL(dest, $('public.heic'), 1, null);
if (destimg) {
    $.CGImageDestinationAddImage(destimg, image, null);
    let res = $.CGImageDestinationFinalize(destimg);
    console.log(res);
} else {
    console.log('error');
}

後は同じですね。
PyObjC を先にやらなかったら文字列ということに気が付かなかったかも。
HEIF にもコレで変換可能でした。

#!/usr/bin/osascript

// webp is bad

ObjC.import('Cocoa');
ObjC.import('Quartz');

let src = $.NSURL.fileURLWithPath('panaleica.heic');
let dest = $.NSURL.fileURLWithPath('panaleica.webp');

let isr = $.CGImageSourceCreateWithURL(src, null);
let image = $.CGImageSourceCreateImageAtIndex(isr, 0, null);

let destimg = $.CGImageDestinationCreateWithURL(dest, $('org.webmproject.webp'), 1, null);
if (destimg) {
    $.CGImageDestinationAddImage(destimg, image, null);
    let res = $.CGImageDestinationFinalize(destimg);
    console.log(res); // false
} else {
    console.log('error');
}

ただ WebP から変換はできるけど WebP へはどちらでも変換できなかった。
多分 Objective-c や Swift でも、public.*** しか駄目なのだろうか?
一番やりたいことなのに、もう少し調べます。

ところで、こんなことをやって今頃知ったんですけど。
UTTypeCreatePreferredIdentifierForTag が廃止になっています。

UTTypeCreatePreferredIdentifierForTag | Apple Developer Documentation

筆者が書いた UTI を調べるコマンドが使えなくなっていて焦った。
というか UTI を調べるコードを書いているブログは全滅だ、わーい!
上記で使おうとして動かなかった、新しい方法を探さなくちゃ。