Similar image @ Gjs

あるアプリを作るのに近似画像を探す処理が必要になった。
Lab色空間 – Wikipedia
を得て元画像の全体ピクセル近似値を調べればいいと解った。

[PHP]似た画像を検索して近い順番に並べる(類似画像検索) | PHP Archive
手段を見つけた、PHP だけど計算方法はどの言語でも同じだろう。
ただ GD を利用しているので他言語では別の画像解析手段が必要。

ということで、同様な処理を Gjs(Linux) でやってみる。
後で osascript(Mac) 版や Web 版を作りたくなった場合に流用できるので。

Gjs だから画像は当然 GdkPixbuf を GD の代わりに使う。
とはいえ GD と同じ関数があるはずもなく違う手段で同様な処理を作る。

画像処理のサンプル一覧
imagecolorat は gdk_pixbuf_get_pixels からオフセット。
imagecolorsforindex は rowstride 値を使って取得。
実は随分前からコレで詰まっていたおかげでブログの更新が…

正直上手く変換できた自信は無いけどなんとかなったっぽいコード。

#!/usr/bin/gjs

const GdkPixbuf = imports.gi.GdkPixbuf;

// 比較元ファイル
let sample = GdkPixbuf.Pixbuf.new_from_file("nae.jpg");
let sample_lab = getImageLab(sample);

// 比較対象ファイル名の配列
let datas = ["nae01.jpg","nae02.jpg","nae03.jpg","nae04.jpg","nae05.jpg"];

let diff = [];
datas.forEach(function(element, index) {
    let data = GdkPixbuf.Pixbuf.new_from_file(element);
    let lab = getImageLab(data);
    // 比較
    let distance = 0;
    sample_lab.forEach(function(element, index) {
        distance += getLabDistance(element, lab[index]);
    });
    diff.push({n:distance, name:datas[index]});
});

// sort
diff.sort(function(a, b) {
    return a.n > b.n;
});

// 確認
diff.forEach(function(element, index) {
    print(element.name, element.n);
});

// 画像をリサイズしピクセルごとのLab色空間上の座標を取得する
function getImageLab(image){
    let thumb = image.scale_simple(4, 4, GdkPixbuf.InterpType.BILINEAR)
    let pixeldata = thumb.get_pixels();
    let rowstride = thumb.get_rowstride();
    let lab = [];
    for(let x=0; x<4; x++){
        for(let y=0; y<4; y++){
            let i = y * rowstride + x * 3;
            lab.push(rgb2lab([pixeldata[i], pixeldata[i+1], pixeldata[i+2]]));
        }
    }
    return lab;
}

// xyz色空間上の座標をlab色空間上の座標に変換する
function xyz2lab(xyz) {
    let threshold = 0.008856;
      
    //Chromatic Adaptation Matrices
    // D50
    let ref_x = 0.96422;
    let ref_y = 1.0000;
    let ref_z = 0.82521;
 
    let var_x = xyz[0] / (ref_x * 100);
    let var_y = xyz[1] / (ref_y * 100);      
    let var_z = xyz[2] / (ref_z * 100);
      
    var_x = (var_x > threshold) ? var_x = Math.pow(var_x, 1/3 ) : (7.787 * var_x) + (16 / 116);
    var_y = (var_y > threshold) ? var_y = Math.pow(var_y, 1/3 ) : (7.787 * var_y) + (16 / 116);
    var_z = (var_z > threshold) ? var_z = Math.pow(var_z, 1/3 ) : (7.787 * var_z) + (16 / 116);
      
 
    let l = ( 116 * var_y ) - 16;
    let a = 500 * ( var_x - var_y );
    let b = 200 * ( var_y - var_z );
    return [l, a, b];
}
 
// rgb値をxyz色空間上の座標に変換する
function rgb2xyz(rgb) {
    let r = rgb[0] / 255;
    let g = rgb[1] / 255;
    let b = rgb[2] / 255;
 
    r = (r > 0.04045) ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
    g = (g > 0.04045) ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
    b = (b > 0.04045) ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
 
    r = r * 100;
    g = g * 100;
    b = b * 100;
      
    let xyz = new Array();
      
    //sRGB D50
    xyz.push(r * 0.4360747 + g * 0.3850649 + b * 0.1430804);
    xyz.push(r * 0.2225045 + g * 0.7168786 + b * 0.0606169);
    xyz.push(r * 0.0139322 + g * 0.0971045 + b * 0.7141733);
    return xyz;
}
 
// rgb値をlab色空間上の座標に変換する
function rgb2lab(rgb) {
    let xyz = rgb2xyz(rgb);
    let lab = xyz2lab(xyz);
    return lab;
}
 
// 2つの座標を比較し距離を返す
function getLabDistance(p1, p2){
    let dist = Math.sqrt( Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2) + Math.pow(p2[2] - p1[2], 2) );
    return dist;
}

similar_pic

苗ちゃんがカワイイのは当然でどうでもよくて。
同一画像なら完全にゼロ、後は色が似た順に取得できた。
GdkPixbuf なので C や Python でも同じことができるはず。

後は osascript はともかく Web で画像解析はどうするかだ。
というか Web アプリでやりたいんですけどね。

Smart Phone Auto Link

iOS 9.0.1 にアップデートが早速きた。
window.innerWidth 初期値の不具合は直っていなかった。
うーん、やっぱり対策しなきゃダメか。

面倒だから当面は全部モバイル用 CSS にする。
いや、上手い回避法が思いつかないだけです。

色々調べているうちに面白いことを見つける。

自動リンクの確認

住所をタップで Map が開くのって便利そうだな。
iPhone でもやってほしいな日式住所対応で。

てか Google ってココまでやるのかと。
スマホのアプリ作りは個人じゃ話にならんレベルになっているや。

iOS9 Safari Bug

iPhone を iOS9 にアップデート。
まずは Safari で我がサイトの表示確認だな。

ios9_1

ナンジャコリャ!
window.innerWidth が仕事をしていないようだ。

そうだ、ソレを確認できるページを自分で作っていたじゃないか。
画面サイズ取得

ios9_2

上段が縦持ちで表示した時、下段は一度横向きにして戻した時。
一度横向きにすれば iOS8 までと同じ値になるようだ。
window.innerWidth | PaePoi

完全にバグですね。
しかしこれじゃ一時凌ぎの手段も無いぞ。

WordPress のほうは Google 公式プラグインなので問題無し。
本サイトのほうは別の手段でも用意するかな、とほほ。
久々の更新がそれだけってのもアレなので何か…

PyObjC

PyObjC で Python から Cocoa にアクセスしてみよう (フェンリル | デベロッパーズブログ)

なんだよ、Mac で Python は全然役立たずじゃないヤン!
ただ Python2 専用だろうな、次期版は Python3 になるかな。

Simple PyObjC Example ? Python recipes ? ActiveState Code

Window も作れ、更にサブクラスも当然のように使えるようだ。
しかし日本語情報が全然見つからない。
日本人は Mac で Python でも計算ばかりやって喜んでいるようだ。
何が面白くてプログラミングしているか理解できないが人それぞれかと。

よし上記をコピーして動かしてみよう。

拡張子を外して実行パーミッションを付けて。
シバンで pythonw を指定してと。

#!/usr/bin/pythonw
#-*- coding:utf8 -*-

これで Finder から command+O してみる。

mac_pythonw

端末エミュレーターが開くジャン!
pythonw は Windows と仕様が違うの?

Macintosh で Python を使う ? Python 2.7ja1 documentation
Windows で Python を使う ? Python 2.7ja1 documentation

確かに Mac のドキュメントには”ターミナル無し”とは書いていない。
ヤラレタって感じ、結局アップルスクリプトと変わらない結果に。
オマケに初期化が凄く遅い、おまえは IronPython か!

やはりスクリプトで GUI はあきらめたほうが良さげ。
でも Objective-C 関数が Python で使えるのは嬉しいかも。
いや、初期化が遅いと結局使わなくなるのは経験済みで。
あー”コレだ!”てのがまだ見つからないYO!

Mac Application System Event

mac にてスクリプトのみで GUI がやりたい。
つまり Linux の PyGObject のようにしたい。

AppleScript: Graphic User Interface (GUI) Scripting

やっとヒントが見つかった。
System Event で Application を作れば空のアプリになるようだ。
コレを利用して別途で GUI を作ればなんとかなりそう。

シバンで osascript を指定して実行パーミッションを付けて。
拡張子を取っ払わないとデフォルトアプリ起動になるので注意。

#!/usr/bin/osascript -l JavaScript

var app = Application("System Events");
app.includeStandardAdditions = true;
app.displayDialog("はろーわーるど");

javascript_dlg

よし、ダイアログのみを出すスクリプトはイケた!
でも Finder にて command+O では端末が開く…

Python でも試したけど拡張子が無ければシバンで開いてくれる。
けれど Windows と同様に端末が開いてしまう。
pythonw がある時点で気がつけよ俺、Linux には無いてか不要。

端末を開かずに osascript を実行する方法、あるのか?
Windows で PowerShell をブン投げた時を再現するとは。

Mac は想像していたほど Linux 的には使えないかも。